ES6 新特性介绍

1、ECMAScript 与JavaScript

  • ECMAScript 也是一门脚本语言,就是JavaScript的语言本身,通常被看作JavaScript的标准化规范,实际上JavaScript是ECMAScript的扩展语言。
  • 因为ECMAScript 中只提供了最基本的语法,而JavaScript实现了ECMAScript 这种语言的标准,并且在此基础之上,做了些扩展。
  • 总之,在浏览器中的JavaScript = ECMAScript + web APIS提供的(BOM+DOM);在node环境中,JavaScript = ECMAScript + Node APIS(fs,net…)。ES6 新特性介绍_第1张图片ES6 新特性介绍_第2张图片

2、ECMAScript 的发展过程

  • 从2015年开始ES保持每年一个版本的迭代,并且从2015年开始,ECMAScript 决定不再按照版本号命名,而是使用发行年份命名。由于这样的决定是在ES2015诞生的过程当中产生的,所以很多人也习惯把ES2015称之为ES6。ES6 新特性介绍_第3张图片

3、ECMAScript 2015/ES6的新特性

重点介绍ES5.1基础之上的变化,这些变化可归纳为4类:

  • 解决原有语法上的一些问题或者不足
  • 对原有语法进行增强,使之变得更为便捷、易用
  • 全新的对象、全新的方法、全新的功能
  • 全新的数据类型和数据结构

let与块级作用域

  • 作用域:某个成员能够起作用的范围。
  • 在ECMAScript 2015之前,ES只有两种作用域(全局作用域、函数作用域)。
  • 在ECMAScript 2015中,新增了块级作用域。块:代码中用一对花括号{}包裹起来的范围。例如:
//块级作用域--if语句
if (true) {
    console.log('hello, baby');
}

//块级作用域--for循环
for (let i = 0; i < 8; i++) {
    console.log(i);
}
  • 以前块是没有独立的作用域的,导致我们在块中定义的成员,外部也可以访问到,这点对于复杂代码是非常不利的,也是非常不安全的。
if (true) {
    var bar = 'hello'
}
console.log(bar); //hello
  • 有了块级作用域,使用let定义变量,就解决了上述问题。
if (true) {
    let bar = 'hello'
}
console.log(bar); //ReferenceError: bar is not defined
  • let这个关键字的特性非常适合我们声明for循环中的计数器。
//for循环内部有两层作用域
for (let i = 0; i < 3; i++) {
    for (let i = 0; i < 3; i++) {
        console.log(i);
    }
    console.log('内存结束 i = ' + i);
}
// 内存结束 i = 0
// 0
// 1
// 2
// 内存结束 i = 1
// 0
// 1
// 2
// 内存结束 i = 2
  • let不存在变量声明的提升。必须先声明再使用。
console.log(foo);  //ReferenceError: Cannot access 'foo' before initialization
let foo = 'aa'

const

  • ES2015中新增了一个const关键字,用来声明一个只读的恒量/常量。
  • 它的特点就是在let的基础上多了【只读】特性,只读就是变量一旦声明过后就不允许再被修改。
const name = 'aaa'
name = 'bbb'  //TypeError: Assignment to constant variable.
  • 既然const是恒量,那么const在声明的同时,就必须要去设置一个初始值。而且const不能像var一样,声明和赋值不能放在两个语句中。
  • const所声明的成员不能被修改,只是说不允许在声明过后,重新去指向一个新的内存地址,并不是说不允许修改恒量中的属性成员。
const obj = {}
obj.name = 'eeee'
console.log(obj); //{ name: 'eeee' }

obj = {}  //TypeError: Assignment to constant variable.

最佳实践:不用var,主用const,配合let 。

数组的解构

通过数组解构的方式,可以从数组当中去快速提取数组中的成员。(与元素的顺序有关,需通过元素的位置去匹配)

//数组解构
const arr = [2, 4, 5]

//传统做法
const foo = arr[1]
console.log(foo);  //4

//数组解构方式
const [, baz] = arr
console.log(baz); //4

//剩余参数
const [f, ...rest] = arr
console.log(f);  //2
console.log(rest);  //[ 4, 5 ]

//超过
const [one, two, three, more] = arr
console.log(more);  //undefined

//默认值
const [a, b, c, d = 'ok'] = arr
console.log(d);  //ok

//适用场景-如拆分字符串
const path = '/foo/bar/baz'
// const tmp = path.split('/')
// const dir = tmp[1]

const [, dir] = path.split('/')
console.log(dir);  //foo

对象的解构

通过对象解构的方式,可以从对象当中去快速提取对象中的属性。(与属性的顺序无关,只需通过属性名去匹配)

//对象解构
const obj = { age: 18, class: '科技一班' }
// const obj = { age: 18 }
const { age } = obj
console.log(age);  //18

//属性重命名
const { age: myAge } = obj
console.log(myAge);  //18

//默认值
// const { class: myClass = '太空二班' } = obj
// console.log(myClass); //太空二班

//适用场景-简化console.log方法
const { log } = console
log('foo')
log('aaa')
log('bbb')
// foo
// aaa
// bbb

模板字符串字面量

模板字符串需要使用`来表示。模板字符串的新特性:

  • 支持换行,可以直接在模板字符串中敲入换行符
//支持换行
const str = `hello, 2020,
we are here`
console.log(str);
// hello, 2020,
// we are here
  • 支持插值表达式方式去字符串中插入JS表达式
//支持插入JS表达式
const name = 'jack'
const msg = `hey, ${name}, welcome to ${new Date().getFullYear()}`
console.log(msg);
//hey, jack, welcome to 2020
  • 带标签的模板字符串:作用是对模板字符串进行加工
//带标签的模板字符串
const str = console.log`hello`  //[ 'hello' ]

const name = 'rock'
const gender = true

function myTagFunc(strings, name, gender) {
    // console.log(strings, name, gender);  //[ 'hey,', ' is a ', '' ] rock male
    // return 123
    const sex = gender ? 'man' : 'woman'
    return strings[0] + name + strings[1] + sex + strings[2]
}

const res = myTagFunc`hey,${name} is a ${gender}`
// console.log(res);  //123
console.log(res);  //hey,rock is a man

字符串的扩展方法

  • includes()
  • startsWith()
  • endsWith()
//字符串的扩展方法
const msg = 'Error: a is not defined.'

console.log(msg.startsWith('Error')); //true
console.log(msg.endsWith('ok')); //false
console.log(msg.includes('a')); //true

函数参数的默认值

//函数参数的默认值
function foo(bar, enable = true) {
    console.log(bar, enable);
}
foo(3) //3 true
foo(3, false) //3 false

剩余参数

ES2015新增了个…操作符,有两个作用:

  • rest作用,即剩余操作符
//以前的做法
function foo() {
    console.log(arguments);
}
foo(1, 2, 3)  //[Arguments] { '0': 1, '1': 2, '2': 3 },伪数组

//剩余参数做法
function bar(first, ...args) {
    console.log(first, args);
}
bar(3, 4, 5)  //3 [ 4, 5 ]
  • spread作用,即展开操作符
//展开数组参数
const arr = ['foo', 'bar', 'baz']

console.log(
    arr[0],
    arr[1],
    arr[2]
);
//foo bar baz

//以前做法
console.log.apply(console, arr);  //foo bar baz
//ES6
console.log(...arr);  //foo bar baz

箭头函数

//以前定义函数
function func(num) {
    return num + 2
}
//箭头函数
const func1 = n => n + 2

console.log(func(3));  //5
console.log(func1(3));  //5

//使用箭头函数大大简化了回调函数的编写
const arr = [3, 2, 4, 6, 7]
const even = arr.filter(function (item) {
    return item % 2
})

console.log(even); //[ 3, 7 ]

const even1 = arr.filter(item => item % 2)
console.log(even1); //[ 3, 7 ]

箭头函数会让我们的代码变的更简短而且更易读,并且,箭头函数不会改变this的指向。

const person = {
    name: 'Rock',
    sayHi1: function () {
        console.log(`Hi, this is ${this.name}`);
    },
    sayHi2: () => {
        console.log(`Hi, this is ${this.name}`);
    },
    sayHiAsync1: function () {
        setTimeout(function () {
            console.log(this.name);
        }, 1000)
    },
    sayHiAsync2: function () {
        setTimeout(() => {
            console.log(this.name);
        }, 1000)
    }
}
person.sayHi1()  //Hi, this is Rock
person.sayHi2()  //Hi, this is undefined
person.sayHiAsync1()  //undefined
person.sayHiAsync2()  //Rock

对象字面量增强

ECMAScript 2015升级了对象字面量的语法。

//对象字面量
const bar = 123
const obj = {
    foo: 34,
    bar,
    func: function () {
        console.log(this.foo);
    },
    func2() {
        console.log(this);
    },
    [bar]: 888
}

//可以使用表达式返回值作为对象的属性名
obj[Math.random()] = 123

console.log(obj); //{ '123':888, foo: 34, bar: 123, func: [Function: func], func2: [Function: func2], '0.5719176856362458': 123}
obj.func2()  //{ '123':888, foo: 34, bar: 123, func: [Function: func], func2: [Function: func2], '0.5719176856362458': 123}

对象扩展方法

  • Object.assign():将多个源对象中的属性复制到一个目标对象中。
    const source = {
        a: 2,
        b: 3
    }
    
    const source1 = {
        c: 222,
        d: 21
    }
    
    const target = {
        a: 888,
        c: 999
    }
    
    const res = Object.assign(target, source, source1)
    console.log(res);  //{ a: 2, c: 222, b: 3, d: 21 }
    console.log(res === target);  //true
    
    //可以用来复制对象
    function func(obj) {
        obj.name = 'func obj'
        console.log(obj);
    }
    
    function func2(obj) {
        const funcObj = Object.assign({}, obj)
        funcObj.name = 'func obj'
        console.log(funcObj);
    }
    
    const obj = { name: 'global obj' }
    // func(obj)  //{ name: 'func obj' }
    // console.log(obj);  //{ name: 'func obj' }
    
    func2(obj)  //{ name: 'func obj' }
    console.log(obj);  //{ name: 'global obj' }
    
  • Object.is():用来去判断两个值是否相等。
    console.log(
        0 == false,  //true
        0 === false, //false
        +0 === -0,  //true
        NaN === NaN,  //false
        Object.is(+0, -0),  //false
        Object.is(NaN, NaN)  //true
    );
    

Proxy(代理对象)

  • 想要监视一个对象中属性的读写,可以使用Object.defineProperty来为对象添加属性。
  • 在ECMAScript 2015中设计了Prixy,专门用来为对象设置访问代理器。可看作门卫,使用它,可以轻松监视到对象的读写过程。
    const person = {
        name: 'Rock',
        age: 5
    }
    
    const personProxy = new Proxy(person, {
        get(target, prop) {
            return prop in target ? target[prop] : 'default'
        },
        set(target, prop, value) {
            if (prop === 'age') {
                //校验
                if (!Number.isInteger(value)) {
                    throw new TypeError(`${value} is not int`)
                }
            }
            target[prop] = value
        }
    })
    
    console.log(personProxy.name); //Rock
    console.log(personProxy.xxx); //default
    
    // personProxy.age = 'ss' //TypeError: ss is not int
    personProxy.age = 20
    console.log(personProxy); //{ name: 'Rock', age: 20 }
    
  • Proxy的优势:
    1. Proxy更为强大:defineProperty只能监视属性的读写,Proxy能够监视到更多对象操作。
      //delete操作
      const personProxy2 = new Proxy(person, {
          defineProperty(target, prop) {
              delete target[prop]
          }
      })
      
      delete personProxy.age
      console.log(person);  //{ name: 'Rock' }
      
      Proxy的其他对象操作 : ES6 新特性介绍_第4张图片
    2. Proxy更好的支持数组对象的监视
      //数组操作
      const list = []
      const listProxy = new Proxy(list, {
          set(target, prop, value) {
              console.log('set', prop, value);
              target[prop] = value
              return true
          }
      })
      listProxy.push(100)
      // set 0 100
      // set length 1
      
    3. Proxy是以非侵入的方式监管了对象的读写。

Reflect

  • 统一的对象操作API。
  • Reflect属于一个静态类,不能通过new Reflect()方式去构造一个实例对象,只能去调用静态类中的方法,如Reflect.get()。
  • Reflect内部封装了一系列对对象的底层操作,提供了13个静态方法。
  • Reflect成员方法就是Proxy处理对象的默认实现。
    const obj = {
        a: 123,
        b: '346'
    }
    
    const proxy = new Proxy(obj, {
        get(target, prop) {
            return Reflect.get(target, prop)
        }
    })
    
    console.log(proxy.a);  //123
    
  • 统一提供了一套用于操作对象的API
    const obj2 = {
        name: 'Rock',
        age: 20
    }
    //传统做法
    console.log('name' in obj2);  //true
    console.log(delete obj2['age']);  //true
    console.log(Object.keys(obj2));  //[ 'name' ]
    
    //Reflect方式
    console.log(Reflect.has(obj2, 'name')); //true
    console.log(Reflect.deleteProperty(obj2, 'age'));  //true
    console.log(Reflect.ownKeys(obj2));  //[ 'name' ]
    
    Reflect的对象操作: ES6 新特性介绍_第5张图片

Promise

提供了一种更优的异步编程解决方案,通过链式编程方式解决了传统异步编程中回调函数嵌套过深的问题。

class类

在此之前,ECMAScript中都是通过定义函数以及函数的原型对象来去实现的类。

//传统实现类--通过函数及函数的原型对象
function Person(name) {
    this.name = name
}
Person.prototype.say = function () {
    console.log(`hi, my name is ${this.name}`);
}

const person = new Person('Jack')
person.say()  //hi, my name is Jack

//ES6实现
class Person1 {
    constructor(name) {
        this.name = name
    }
    say() {
        console.log(`hi, my name is ${this.name}`);
    }
}
const per = new Person1('Rock')
per.say()  //hi, my name is Rock

静态方法和实例方法
实例方法:需要通过这个类构造的实例对象去调用。
静态方法:直接通过这个类本身去调用。
ES2015中新增添加静态成员与静态方法的static关键字。

//静态方法和实例方法
class Person {
    constructor(name) {
        this.name = name
    }
    //实例方法
    say() {
        console.log(`hi, my name is ${this.name}`);
    }
    //静态方法
    static create(name) {
        return new Person(name)
    }
}
const tom = Person.create('Tom')
tom.say()  //hi, my name is Tom

类的继承
继承是面向对象中一个非常重要的特性。通过继承能够抽象出来相似类型之间重复的地方。
ES2015之前,大多数情况都是使用原型实现继承,但在Es2015中新增关键字extends关键字实现继承。

class Person {
    constructor(name) {
        this.name = name
    }
    //实例方法
    say() {
        console.log(`hi, my name is ${this.name}.`);
    }
}

class Student extends Person {
    constructor(name, age) {
        super(name)
        this.age = age
    }
    hello() {
        super.say()
        console.log(`my age is ${this.age}.`);
    }
}

const student = new Student('Jerry', 6)
student.hello()
// hi, my name is Jerry.
// my age is 6.

Set

可以理解为集合,与传统的数组比较类似,但是Set中的元素不允许重复。每一个值在Set当中都是唯一的。

//Set数据结构
const set = new Set()
set.add(1).add(2).add(3).add(4)
console.log(set);  //Set(4) { 1, 2, 3, 4 }

//遍历
set.forEach(i => console.log(i))
// 1
// 2
// 3
// 4

//for...of遍历
for (let item of set) {
    console.log(item);
}
// 1
// 2
// 3
// 4

//获取set的长度
console.log(set.size);  //4

//判断是否包含某个元素
console.log(set.has(19)); //false

//删除元素
console.log(set.delete(3)); //true

//清空元素
set.clear()
console.log(set);  //Set(0) {}

常见应用场景:为数组去重

//数组去重
const arr = [1, 2, 3, 2, 4, 1, 2, 5]
const result = new Set(arr)
console.log(result);  //Set(5) { 1, 2, 3, 4, 5 }

//set转换为数组
const resArr1 = Array.from(new Set(arr))
const resArr2 = [...new Set(arr)]
console.log(resArr1, resArr2);  //[ 1, 2, 3, 4, 5 ] [ 1, 2, 3, 4, 5 ]

Map

与ECMAScript中对象非常类似,都是键值对集合,ECMAScript中键只能为字符串类型,不方便存放复杂数据结构。

//ES6以前
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{ a: 1 }] = 'value'
console.log(Object.keys(obj));  //[ '123', 'true', '[object Object]' ]
console.log(obj['[object Object]']);  //value

Map用来去映射两个任意类型数据之间的映射关系。与对象最大的区别就是,它可以用任意类型的数据作为键,而对象只能用字符串作为键。

//ES6
const map = new Map()
const jack = { name: 'jack' }
map.set(jack, 90)
map.set('age', 19)
console.log(map);  //Map(1) { { name: 'jack' } => 90, 'age' => 19 }
console.log(map.get(jack));  //90

console.log(map.has(jack));  //true
// console.log(map.delete('age'));  //true
// map.clear()
// console.log(map);  //Map(0) {}

//遍历
map.forEach((value, key) => {
    console.log(value, key);
})
// 90 { name: 'jack' }
// 19 age

Symbol

一种全新的原始数据类型
在ES2015之前,对象的属性名都是字符串,而字符串是有可能重复的,容易引起冲突。
ES2015为了解决这个问题,提供了一种全新的数据类型,叫Symbol(符号)。作用是表示一个独一无二的值。
最主要的作用就是为对象添加独一无二的属性名。

//ES2015以前
//store.js
const cache = {}
//a.js
cache['a_foo'] = Math.random()
//b.js
cache['b_foo'] = '123'

console.log(cache);  //{ a_foo: 0.8887530628553206, b_foo: '123' }

//ES2015
const s = Symbol()
console.log(s);  //Symbol()
console.log(typeof s);  //symbol
console.log(Symbol() === Symbol());  //false

截止到ES2019,一共定义了7种原始数据类型,未来还会新增BigInt原始数据类型,用于去存放更长的数字。目前处于stage-4阶段,预计下个版本被标准化,标准化过后就是8种数据类型了。
每次通过Symbol去创建的值一定是唯一的,如果想全局复用一个相同的Symbol值,可以使用全局变量方式或使用Symbol提供的静态方法:

console.log(Symbol('foo'));  //Symbol(foo)
console.log(Symbol('bar'));  //Symbol(bar)
console.log(Symbol('car'));  //Symbol(car)

const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj);  //{ [Symbol()]: '123', [Symbol()]: '456' }

const obj1 = {
    [Symbol()]: 888
}
console.log(obj1);  //{ [Symbol()]: 888 }

//a.js
const name = Symbol()
const person = {
    [name]: 'abc',
    say() {
        console.log(this[name]);
    }
}
//b.js
person.say()  //abc

const obj2 = {
    [Symbol()]: 'symbol value',
    foo: 'normal value'
}
//不能遍历普通对象
// for (let i of obj2) {
//     console.log(i);  //TypeError: obj is not iterable
// }
console.log(Object.keys(obj2));  //[ 'foo' ]
console.log(JSON.stringify(obj2));  //{"foo":"normal value"}

console.log(Object.getOwnPropertySymbols(obj2));
// {"foo":"normal value"}
// [ Symbol() ]

提供了一些内置的Symbol常量:

  • Symbol.iterator
  • Symbol.hasInstance

for…of

在ECMAScript中遍历数据的方法:

  • for:遍历普通的数组
  • for…in:遍历键值对
  • 一些对象的遍历方法(如 forEach等)
    这些遍历方式都有一定的局限性,E2015引入了全新的for…of循环。这种遍历方式以后会作为遍历所有数据结构的统一方式。
const obj = {
    [Symbol()]: 'symbol value',
    foo: 'normal value'
}
//不能遍历普通对象
// for (let i of obj) {
//     console.log(i);  //TypeError: obj is not iterable
// }

const arr = [23, 45, 67, 87]

// for (const item of arr) {
//     console.log(item);
// }
// 23
// 45
// 67
// 87

//forEach不能跳出循环
// arr.forEach(item => console.log(item))

//for...of能跳出循环
for (const item of arr) {
    console.log(item);
    if (item > 50) {
        break
    }
}
// 23
// 45
// 67

const s = new Set(['aaa', 'bbb'])
for (let item of s) {
    console.log(item);
}
// aaa
// bbb

const m = new Map()
m.set('foo', 123)
m.set('bar', 345)
for (let [key, value] of m) {
    console.log(key, value);
}
// foo 123
// bar 345

可迭代接口

ES中能够表示有结构的数据类型越来越多,为了给各种各样的数据结构提供统一的遍历方式,ES2015提供了Iterable接口,即可迭代的。实现Iterable接口就是for…of的前提。

//迭代器(iterator)
const set = new Set(['aa', 'bb', 'cc'])
const iterator = set[Symbol.iterator]()

console.log(iterator.next());  //{ value: 'aa', done: false }
console.log(iterator.next());  //{ value: 'bb', done: false }
console.log(iterator.next());  //{ value: 'cc', done: false }
console.log(iterator.next());  //{ value: undefined, done: true }

只要对象实现了Iterator,那对象也可以使用for…of方法。

//实现可迭代接口
const obj = {
    //iterable
    [Symbol.iterator]: function () {
        //iterator
        return {
            next: function () {
                //IterationResult
                return {
                    value: 'aaa',
                    done: true
                }
            }
        }
    }
}

const obj2 = {
    store: ['foo', 'bar', 'baz'],

    [Symbol.iterator]: function () {
        let index = 0
        const self = this

        return {
            next: function () {
                const result = {
                    value: self.store[index],
                    done: index >= self.store.length
                }
                index++
                return result
            }
        }
    }
}

for (const item of obj2) {
    console.log('循环体', item)
}
// 循环体 foo
// 循环体 bar
// 循环体 baz

for…of可以取代forEach,而且for…of内可以使用break。

迭代器模式
迭代器的核心就是对外提供统一遍历接口,让外部不用再去关心内部这个数据结构是啥样的。

// 场景:你我协同开发一个任务清单应用
//实现代码
const todos = {
  life: ['阅读', '运动', '音乐'],
  learn: ['JS', '算法', 'Vue'],
  work: ['编码'],
  // 提供统一遍历访问接口
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for (const item of all) {
      callback(item)
    }
  },
  // 提供迭代器(ES2015 统一遍历访问接口)
  [Symbol.iterator]: function () {
    const all = [...this.life, ...this.learn, ...this.work]
    let index = 0
    return {
      next: function () {
        return {
          value: all[index],
          done: index++ >= all.length
        }
      }
    }
  }
}

// for (const item of todos.life) {
//   console.log(item)
// }
// for (const item of todos.learn) {
//   console.log(item)
// }
// for (const item of todos.work) {
//   console.log(item)
// }

todos.each(function (item) {
  console.log(item)
})

console.log('-------------------------------')

for (const item of todos) {
  console.log(item)
}
// 阅读
// 运动
// 音乐
// JS
// 算法
// Vue
// 编码

生成器(Generator)

  • 避免异步编程中回调嵌套过深,提供更好的异步编程解决方案。
  • 生成器对象其实也实现了Iterator接口协议。
    function* foo() {
      console.log('qqq')  //qqq
      return 100
    }
    
    const result = foo()
    console.log(result.next())  //{ value: 100, done: true }
    
  • 生成器一般与yield关键字配合使用。
    function* foo() {
      console.log('1111')
      yield 100
      console.log('2222')
      yield 200
      console.log('3333')
      yield 300
    }
    
    const generator = foo()
    
    console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
    console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
    console.log(generator.next()) // 。。。
    console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
    // 1111
    // { value: 100, done: false }
    // 2222
    // { value: 200, done: false }
    // 3333
    // { value: 300, done: false }
    // { value: undefined, done: true }
    
  • 生成器函数会自动帮我们返回一个生成器对象,调用该对象的next方法才会让这个生成器函数的函数体开始执行,遇到yield函数执行会暂停下来。yield后的值会作为next()的返回值。
    // Generator 应用
    // 案例1:发号器
    function* createIdMaker() {
      let id = 1
      while (true) {
        yield id++
      }
    }
    
    const idMaker = createIdMaker()
    
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    // 1
    // 2
    // 3
    // 4
    
    // 案例2:使用 Generator 函数实现 iterator 方法
    const todos = {
      life: ['阅读', '运动', '音乐'],
      learn: ['JS', '算法', 'Vue'],
      work: ['编码'],
      [Symbol.iterator]: function* () {
        const all = [...this.life, ...this.learn, ...this.work]
        for (const item of all) {
          yield item
        }
      }
    }
    
    for (const item of todos) {
      console.log(item)
    }
    // 阅读
    // 运动
    // 音乐
    // JS
    // 算法
    // Vue
    // 编码
    

惰性执行。

4、ECMAScript 2016概述

发布于2016/6,是个小版本,仅实现了两个特性:

  • includes()
    const arr = [2,3,'aa',4]
    console.log(arr.indexOf(2));  //0
    console.log(arr.includes(1));  //-1
    console.log(arr.includes(2));  //true
    
  • 指数运算符
    console.log(Math.pow(2,10));  //1024
    console.log(Math.pow(2,10));  //1024
    

5、ECMAScript 2017概述

发布于2017/6,也是小版本,带来了新功能:

  • Object对象的三个方法
    Object.values()、Object.entries()、Object.getOwnPropertyDescriptors()(获取对象中完整信息的描述)
  • 字符串填充方法
    String.prototype.padStart()、String.prototype.padEnd()
  • 允许函数参数中添加尾逗号
  • Async/Await

附录

  • ECMAScript 2015 Language Specification
  • Flow
  • Flow-type

你可能感兴趣的:(笔记,javascript,es6)