目录
- let/const
- 解构
- 箭头函数
- 模板字符串
- 默认参数和rest参数(不具名参数)
- 延展操作符/扩展运算符
- Promise对象
- Class(类)
- Set和Map
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。
let/const
let类似于var,区别在于let所声明的变量只在let命令所在的代码块内有效。const声明的是常量。一旦声明,常量的值就不能改变。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
let/const与var的区别:
1)let/const提供了块级作用域以及变量创建时不会立即初始化。
2)在同一个作用域内let/const禁止重复声明相同的变量。
3)let/const不存在变量提升,它所声明的变量一定要在声明后使用,否则报错。
4)暂时性死区:在当前作用域,使用的变量已经存在,但是在代码执行到变量声明前禁止访问。
解构
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。也就是说通过模式匹配来进行变量赋值。
数组解构
let [a, b, c] = [1, 2, 3];
// 不完全解构
let [first, , , last] = ["one", "two", "three", "four"];
// 交换变量的值
let x = 1;
let y = 3;
[x, y] = [y, x];
// 嵌套解构
let [a, [b], d] = [1, [2, 3], 4];
console.log(a); // 1
console.log(b); // 2
console.log(d); // 4
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象解构
let { foo, bar } = { foo: "aaa", bar: "bbb" };
// 嵌套解构
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
console.log(line); // 1
console.log(loc); // Object {start: Object}
console.log(start); // Object {line: 1, column: 5}
箭头函数
ES6中新增箭头操作符用于简化函数的写法,操作符左边为参数,右边为具体操作和返回值
let fn = (a, b) => {
return a + b;
}
//等同于
let fn = function(a, b) {
return a + b;
};
// 如果不使用花括号表达式将作为函数的返回值
let fn = (a, b) => a + b;
// 如果只有单个参数=>之前的括号也可以省略
let fn = a => a + 1;
不同于普通函数,箭头函数的this指向不是调用它的对象,而是指向词法作用域,就是定义时所在的对象。这是因为箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。
let fn = {
type: "animal",
says () { // 注意:这里 says () {} 不是箭头函数 是says: function () {}的简写
let Fn = () => `this.type:${this.type}`; // this指向fn对象
return Fn();
}
};
fn.says(); // "this.type:animal"
同时箭头函数也没有自己的arguments,需要以rest参数来替代。
模板字符串
当需要拼接大量字符串时,传统的写法非常麻烦。模板字符串用反引号`来标识起始,用${}来引用变量,所有的空格和缩进都会被保留在输出之中。
let num = 9;
// 不使用模板字符串:
let str = "The num: " + num + "."
// 使用模板字符串:
let str = `The num: ${ num }.`
默认参数和rest参数(不具名参数)
默认参数设置参数默认值。
let fn = (type) => {
type = type || "animal";
console.log(type);
};
// 用es6可以这么写
let fn = (type = "animal") => {
console.log(type);
};
rest参数,也叫做剩余参数。是将传入的未具名的参数作为一个数组集合。
let fn = (arg, ...restArg) => {
console.log(arg, restArg);
};
fn(1, 2, 3); // 1, [2, 3]
延展操作符/扩展运算符
扩展运算符和rest参数一样,也是三个点。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
// 数组展开
let arr = [8, ...[1, 2, 3, 4, 5]]; // [8, 1, 2, 3, 4, 5]
// 复制数组
const a1 = [1, 2];
const a2 = [...a1]; // 或 const [...a2] = a1;
// 合并数组
const arr1 = ["a", "b"];
const arr2 = ["c"];
const arr3 = [...arr1, ...arr2]; // 等同与arr1.concat(arr2);、
// 将字符串转为真正的数组
let newArr = [..."hello"]; // [ "h", "e", "l", "l", "o" ]
// 对象展开
let obj = { "foo": "bar", "x": 42 };
console.log({ ...obj }); // {foo: "bar", x: 42}
Promise对象
Promise对象是一个构造函数,用来生成Promise实例。Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
const promise = new Promise((resolve, reject) => {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then((value) => {
// success
}, (error) => {
// failure
});
Class(类)
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
class Animal {
// 构造方法
constructor(){
// this关键字代表实例对象
this.type = "animal";
}
// constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的
says(say){
console.log(this.type + " says " + say)
}
}
// Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。
class Cat extends Animal {
constructor(){
// super关键字指代父类的实例(即父类的this对象)
super(); // 子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。所以子类必须在constructor方法中调用super方法,否则将获取不到this对象。
this.type = "cat";
}
}
let cat = new Cat();
cat.says("hello"); //cat says hello
Set和Map
ES6中增加了两个新的数据结构:Set和Map。Set是不包含重复值的列表,而Map则是键与相对应的值的集合。
Set类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);
console.log([...set]); // [1, 2, 3, 4]
// 去除数组重复成员
console.log([...new Set([1, 2, 3, 4, 4])]);
// 字符串去重
console.log([...new Set('ababbc')].join(''));
Set实例有四个操作方法(add()、delete()、has()、clear())和一个属性(size)
let s = new Set();
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size; // 2
s.has(1); // true
s.has(2); // true
s.has(3); // false
s.delete(2);
s.has(2); // false
s.clear()
s.size // 0
Set 结构的实例有四个遍历方法,可以用于遍历成员:三个遍历器生成函数(keys()、values()、entries())和一个遍历方法(forEach())
keys方法、values方法、entries方法返回的都是遍历器对象(详见Iterator)。都这可以使用遍历器对象的方法for…of进行遍历。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。forEach方法与ES5数组的forEach类似。
let set = new Set(['red', 'green', 'blue'])
for (let item of set.keys()) {
console.log(item)
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item)
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item)
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach((value, key) => console.log(value, key))
Set转化为数组,有两种方法:…扩展运算符和Array.from()
// 方法一
let set = new Set([1, 2, 3])
set = new Set([...set].map(val => val * 2))
// set的值是2, 4, 6
// 方法二
let set = new Set([1, 2, 3])
set = new Set(Array.from(set, val => val * 2))
// set的值是2, 4, 6
Map是一种由键值对集合构成的数据结构,类似于对象,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
const m = new Map();
const o = {"p": "Hello World"};
m.set(o, "content");
m.get(o); // "content"
m.has(o); // true
m.delete(o); // true
m.has(o); // false
Map构造函数可以接收任何一个具有Iterator接口、且每个成员都是一个双元素的数组的数据结构作为参数,这就是说,Set和Map都可以用来生成新的 Map。
const set = new Set([
["foo", 1],
["bar", 2]
]);
const m1 = new Map(set);
m1.get("foo"); // 1
const m2 = new Map([["baz", 3]]);
const m3 = new Map(m2);
m3.get("baz"); // 3
Map 结构的实例有5个操作方法(set(key, value)、get(key)、has(key)、delete(key)、clear())和一个属性(size)
let map = new Map()
.set(1, "a")
.set(2, "b")
.set(3, "c");
map.size; // 3
map.get(3); // "c"
map.has(2); // true
map.has('years'); // false
map.delete(1);
map.has(1); // false
map.clear();
map.size; // 0
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员。
const map = new Map([
["F", "no"],
["T", "yes"],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
WeakSet结构与Set类似,也是不重复的值的集合。但是WeakSet 的成员只能是对象,而不能是其他类型的值。而且WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
WeakMap结构与Map结构类似,也是用于生成键值对的集合。但是WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。而且WeakMap的键名所指向的对象,不计入垃圾回收机制。