# 重学 ES6
# 块级作用域变量
let word1 = 'hello'
const word2 = 'world'
2
let 用于声明变量 const 用于声明常量,常量一旦声明便无法更改
使用 let 和 const 声明的变量是有块级作用域(函数体,判断体,循环体等)
因此建议使用 es6 语法中 let const 来定义变量
# 模板字符串代替字符串拼接
const a = 'hello'
const b = 'world'
// in ES5
const c = a + ' ' + b
// in ES6 used 模板字符串
const d = `${a} ${b}`
2
3
4
5
6
7
8
# padStart()、padEnd()
新的字串填充方法,可以从start位置或是end位置,开始填充到指定数字长度,被填充的内容为第二个参数。
let astring = 'moment'
console.log(astring.padStart(10,'m'))
// 'mmmmmoment'
console.log('abc'.padStart(10, "foo"))
// "foofoofabc"
console.log('abc'.padEnd(10, "foo"))
// 'abcfoofoof'
2
3
4
5
6
7
8
9
# 解构赋值
// in ES5
let firstName = personalInformation.firstName
let lastName = personalInformation.lastName
let city = personalInformation.city
// in ES6 used 对象解构赋值
let { firstName: fn, lastName: ln, city: ct } = personalInformation
2
3
4
5
6
7
从 personalInformation 对象中读取 firstName , lastName , city 几个属性,并用 fn , ln , ct 几个别名作为解构赋值后新变量的名字,如果未声明别名,默认使用 firstName , lastName , city 作为解构后的变量名
// in ES6 used 数组解构赋值
let [firstName, lastName, city] = ['Dylan', 'Israel', 'Austin']
2
使用数组解构赋值时默认为按照数组顺序,逐一读取
# 加强循环
最早的数组遍历方式
var a = ["a", "b", "c"];
for(var index = 0;index < a.length;index++){
console.log(a[index]);
}
2
3
4
自从ES5发布以后,可以用内建的forEach来遍历数组
var a = ["a", "b", "c"];
a.forEach(function(element) {
console.log(element);
});
2
3
4
推荐在循环普通对象属性的时候,使用 for...in 在遍历数组的时候的时候使用 for...of
for...in循环出的是key ,for...of循环出的是value
for...of 不能循环普通的对象 需要通过和 Object.keys() 搭配使用
# 散布运算符 与 rest 参数
# 散布运算符
const t = [1, 2, 3, 4, 5]
const [first, second, ...rest] = t
console.log(first, second) // 1, 2 is printed
console.log(rest) // [3, 4 ,5] is printed
2
3
4
5
6
上面语法中的三个点 (...) 是散布运算符,其目标选定是特定变量中的整个值。
# rest 参数
实际使用中经常遇到可变参数的情况,以定义函数实现计算传入所有参数的和为例.
使用arguments参数
**function** sumArgu () {
**var** result **=** 0;
**for** (**var** i **=** 0; i **<** arguments.length; i**++**) {
result **+=** arguments[i];
}
**return** result
}
console.log(sumArgu(1,2,3));*//6*
2
3
4
5
6
7
8
使用rest参数
**function** sumRest (...m) {
**var** total **=** 0;
**for**(**var** i **of** m){
total **+=** i;
}
**return** total;
}
console.log(sumRest(1,2,3));*//6*
2
3
4
5
6
7
8
上述两种方法看似差不多,但是操作的元素有本质的区别,其中arguments是一个类数组,本质是对象;而rest参数m,是真正的数组,可以正常调用数组的所有方法.所以在某些场景中,无需将arguments转为真正的数组,可以直接使用rest参数代替
# includes()方法
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
2
3
第一个参数是要查找的元素
第二个参数表示搜索的起始位置,默认为 0 。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为 -4 ,但数组长度为 3 ),则会重置为从 0 开始。
[NaN].indexOf(NaN)
// -1
includes使用的是不一样的判断算法,就没有这个问题。
[NaN].includes(NaN)
// true
2
3
4
5
6
7
8
9
对比indexof方法
indexOf方法有两个缺点
一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于 -1 ,表达起来不够直观。
二是,它内部使用严格相当运算符( === )进行判断,这会导致对NaN的误判。
# import & export 方法
ES6中export和import一般的用法有两种
- 命名导出(Named exports)
- 默认导出(Default exports)
// 导出单个特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}
// 导出列表
export { name1, name2, …, nameN };
// 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };
// 解构导出并重命名
export const { name1, name2: bar } = o;
// 默认导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
// 导出模块合集
export * from …; // does not set the default export
export * as name1 from …; // Draft ECMAScript® 2O21
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 命名导出
//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function diag(x, y) {
return sqrt(square(x) + square(y));
}
export {sqrt, square, diag}
// 把export直接加到声明前面就可以省略{}
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
无论怎样导出,引入的时候都需要{}
// 别名导入
import {speak as cowSpeak} from './cow.js'
import {speak as goatSpeak} from './goat.js'
2
3
别名导入使用 变量名 as 别名
导入对象
当从每个模块需要引入的方法很多的时候,这种写法就显得十分的繁琐、冗长,因此使用命名空间引入
import * as cow from './cow.js'
import * as goat from './goat.js'
cow.speak() // moo
goat.speak() // baa
2
3
4
5
# 默认导出
默认导出就不需要name了,但是一个js文件中只能有一个export default 但也可以导出多个方法。
export default {
speak () {
return 'moo'
},
eat () {
return 'cow eats'
},
drink () {
return 'cow drinks'
}
}
2
3
4
5
6
7
8
9
10
11
引入与命名空间引入类似
import cow from './default-cow.js'
import goat from './default-goat.js'
cow.speak() // moo
goat.speak() // baa
2
3
4
5
# 类 Class
// 在ES6中,我们终于有新的语法糖可以使用class了
class Animal{
constructor(name,color){
this.name = name
this.color = color
}
toString(){
console.log('name:'+this.name+',color:'+this.color)
}
}
const animal = new Animal('dog','white')
console.log(animal.toString()) // "name:dog,color:white"
// 以下为继承
class Cat extends Animal{
constructor(action){
// 记得这边要先super,才可以拿到父的资料
super('cat','white')
this.action = action
}
toString(){
console.log(super.toString())
}
}
const CCat = new Cat('Catch')
console.log(CCat.toString()) // "name:cat,color:white"
console.log(CCat instanceof Cat) // true
console.log(CCat instanceof Animal) // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ES6 提供了 类似于面向对象的声明方法(实现封装继承,提供构造器)
# promise & async & await
# Promise
Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。
本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了。
# async await
在ES6为了要解决回调的问题,出现了Promise的then函数,但当逻辑很多时,,要链式多个then函数,会造成语意不清楚
new Promise((resolve,reject) => { resolve(42)}
.then(() => { do one})
.then(() => { do two})
.catch(() => {console.log(err)})
)
2
3
4
5
在ES8中,把非同步做得更方便,而这其实就是Promise与Generator的组合,而变成语法糖。
async function xxx(){
await do One
await do_Two
}
2
3
4
# Map & Set
JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。
为了解决这个问题,最新的ES6规范引入了新的数据类型Map
# Map
Map是一组键值对的结构,具有极快的查找速度。
var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
2
3
4
初始化Map需要一个二维数组,或者直接初始化一个空Map。
Map具有以下方法:
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
2
3
4
5
6
7
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
2
3
4
# Set
Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Setvar s2 = new Set([1, 2, 3]); // 含1, 2, 3
重复元素在Set中自动被过滤:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
2
数组中数字3和字符串'3'是不同的元素。
Set具有以下方法:
var s = new Set([1, 2, 3]);
// add(key)方法可以添加元素到Set中可以重复添加,但不会有效果:
s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // Set {1, 2, 3, 4}
//delete(key)方法可以删除元素:
s.delete(3);
s;// Set{1, 2, 4}
2
3
4
5
6
7
8
9