ECMAScript 6(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。Mozilla 公司将在这个标准的基础上,推出 JavaScript 2.0。ECMAScript 和 JavaScript 是什么关系?简单来说,ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现。
let 和 const
var 函数作用域 function scope,不在函数内时作用域是全局的
用 let 和 const 声明变量, let, const 块级作用域 block scope,作用域是{}内
eg.执行以下语句判断区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
for (var i = 0; i < 10; i++) {
console.log(i);
setTimeout(function () {
console.log(`i:${i}`);
}, 1000);
}
for (let i = 0; i < 10; i++) {
console.log(i);
setTimeout(function () {
console.log(`i:${i}`);
}, 1000);
}
|
let, const 不能重复声明变量值
- let 声明的变量是可以重新赋值的, const 声明的变量只能修改引用类型的属性值
- 变量提升:let, count 有变量提升,未声明先使用存在临时性死区(Temporal dead zone),详见 mdn
箭头函数
特点:简明的语法,隐式返回(省去 return 关键字),匿名函数
this:普通函数 this 是动态绑定的
1
2
3
4
5
6
7
8
9
10
11
12
|
const Jelly = {
name: "Jelly",
hobbies: ["Coding", "Sleeping", "Reading"],
printHobbies: function () {
// console.log(this);
this.hobbies.map(function (hobby) {
// console.log(this);
console.log(`${this.name} loves ${hobby}`);
});
},
};
Jelly.printHobbies();
|
map 中的回调函数是一个独立的函数,不作为对象的方法,并且没有通过 call bind apply
来改变里面的 this,this 指向 window,严格模式下指向 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const Jelly = {
name: "Jelly",
hobbies: ["Coding", "Sleeping", "Reading"],
printHobbies: function () {
// console.log(this);
var self = this;
this.hobbies.map(function (hobby) {
// console.log(this);
console.log(`${self.name} loves ${hobby}`);
});
},
};
Jelly.printHobbies();
|
箭头函数的 this 值继承父作用域,是词法作用域,定义的时候就指向明确,且不会绑定 this:
1
2
3
4
5
6
7
8
9
10
11
12
|
const Jelly = {
name: "Jelly",
hobbies: ["Coding", "Sleeping", "Reading"],
printHobbies: function () {
// console.log(this);
this.hobbies.map((hobby) => {
// console.log(this);
console.log(`${this.name} loves ${hobby}`);
});
},
};
Jelly.printHobbies();
|
命名函数在递归,事件绑定时有用,在箭头函数中使用:
1
2
3
|
const greet = (name) => {
alert(`Hello ${name}`);
};
|
箭头函数不适用的情况:
- 需要使用 this 慎用
- 需要使用 arguments(箭头函数没有 arguments)
模板字符串
- 模板字符串中的换行和空格都是会被保留的。
- 模板字符串可嵌套,支持三元表达式。
- 标签模板字符串,是一个函数的调用。
1
2
|
alert`Hello world!`;
// 等价于alert('Hello world!');
|
解构赋值
- 针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
- 是对赋值运算符的扩展,方便提取对象属性值,可嵌套可忽略。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
let [a, b, c, d, e] = "hello";
let obj = { p: ["hello", { y: "world" }] };
let {
p: [x, { y }],
} = obj;
// x = 'hello'
// y = 'world'
let obj = { p: ["hello", { y: "world" }] };
let {
p: [x, {}],
} = obj;
// x = 'hello'
|
计算属性
对象字面定义属性名位置的 [ ] 中可以放置任意合法表达式。
1
2
3
4
5
6
7
8
|
const keys = ["name", "age", "birthday"];
const values = ["jelly", 18, "2016-01"];
const Laravist = {
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
};
console.log(Laravist);
|
Symbol
ES6 引入了一种新的原始数据类型表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
用于生成唯一标识符避免命名冲突,可作为私有属性在对象内部使用,不能 for 循环遍历
1
2
3
4
5
6
7
8
9
|
const classRoom = {
[Symbol("lily")]: { grade: 60, gender: "female" },
[Symbol("nina")]: { grade: 70, gender: "female" },
[Symbol("nina")]: { grade: 90, gender: "female" },
};
const syms = Object.getOwnPropertySymbols(classRoom).map(
(sym) => classRoom[sym]
);
console.log(syms);
|
剩余参数
1
2
3
|
const player = ["jelly", 123, 2.4, 3.6, 1.7];
const [name, id, ...scores] = player;
console.log(name, id, scores);
|
扩展运算符可以将可遍历对象元素扩展成新的参数序列,而不用改变原来的对象
1
2
3
4
|
const younger = ["aaa", "bbb", "ccc"];
const older = ["xxx", "yyy", "zzz"];
const members = [...younger, "ddd", ...older];
const newmembers = members;
|
Promise
Promise 用于避免回调地狱
1
2
3
4
5
6
7
8
9
10
|
const p = new Promise((resolve, reject) => {
setTimeout(() => {
reject(Error("Laravist isn't awesome!"));
}, 2000);
});
p.then((data) => {
console.log(data);
}).catch((err) => {
console.error(err);
});
|
await 操作符用于等待一个 Promise 对象,它只能在异步函数 async function 内部使用。
await 针对所跟不同表达式的处理方式:
- Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。
- 非 Promise 对象:直接返回对应的值。
1
2
3
4
5
6
7
8
9
10
11
12
|
function testAwait(x) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function helloAsync() {
var x = await testAwait("hello world");
console.log(x);
}
helloAsync();
|
Class
- class 是语法糖,本质是 funciton,没有变量提升
一个继承的例子:
1
2
3
4
5
6
7
8
|
function MyArray() {
Array.apply(this, arguments);
}
const colors = new MyArray();
colors[0] = "red";
console.log(colors.length); //undefined
colors.length = 0;
console.log(colors[0]); //red
|
ES5 是先新建子类的实例对象 this,
再将父类的属性添加到子类上,原生构造函数会忽略 apply 方法传入的 this,
父类的内部属性无法获取,导致无法继承原生的构造函数。
1
2
3
4
5
6
7
8
9
10
11
|
class MyArray extends Array {
constructor() {
super();
console.log(this);
}
}
const colors = new MyArray();
colors[0] = "red";
console.log(colors.length); // 1
colors.length = 0;
console.log(colors[0]); // undefined
|
ES6 允许继承原生构造函数定义的子类,因为 ES6 是先新建父类的实例对象 this,
然后再用子类的构造函数修饰 this,使得父类的素有行为都可以继承。
ES6 可以自定义原生数据结构的子类,这是 ES5 无法做到的。
一些补充
新增 for…of 循环:
先回顾 js 中 for 循环的几种写法
1
2
3
4
|
const fruits = ["apple", "banana", "orange", "mango"];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
|
可读性不是很好
1
2
3
|
fruits.forEach((fruit) => {
console.log(fruit);
});
|
不能在循环中 break 或 continue
1
2
3
|
for (let index in fruits) {
console.log(fruits[index]);
}
|
会遍历对象上所有可枚举属性
1
2
3
|
for (let fruit of fruits) {
console.log(fruit);
}
|
不会遍历数组中非数字属性,能够 break 或 continue
应用数组解构语法
1
2
3
|
for (let [index, fruit] of fruits.entries()) {
console.log(`${fruit} rank in ${index + 1} in my favorite fruits`);
}
|
for…of 可以应用于可迭代对象(部署了 iterator 接口或提供 Symbol.iterator 方法的数据结构)
数组,字符串,arguments,NodeList,map.set 等,但不支持对象
Array.from()和 Array.of()
es6 新增数组方法 Array.from()和 Array.of():
- 注意是数组原型对象上的静态方法
- Array.from()用于把可迭代对象转化成数组,Array.of()传入参数生成数组
Proxy 与 Reflect
Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。
- Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作。
- ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上。
迭代器与生成器
Iterator 是 ES6 引入的一种新的遍历机制,通过一个键为 Symbol.iterator 的方法来实现。
Generator 函数:在 function 后面,函数名之前有个*,函数内部有 yield 表达式。
Map 与 Set
Object 的键只能是字符串或者 Symbols, Map 的键可以是任意值,Map 中的键值是有序的(FIFO 原则),Map 的键值对个数可以从 size 属性获取。
Set 对象允许你存储任何类型的唯一值,NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个。