目录

  • 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的键名所指向的对象,不计入垃圾回收机制。