ES6 必知必会




by @llh911001

2016/1/13

变量和参数

let 声明

  • 块级作用域

if(true) {
  let a = 1
}
console.log(a)
// a is not defined
              
  • 不可重复声明

let a = 1
let a = 2
// Duplicate declaration "a"
              
  • 以前

for (var i=0; i<10; i++) {
  setTimeout(~function(a) {
    console.log(a)
  }(i), 100)
}

// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
            
  • 现在

for (let i=0; i<10; i++) {
  setTimeout(() => {
    console.log(i)
  }, 100)
}

// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
              

const 声明

  • 定义常量

const CONSTANT = 'a constant'

CONSTANT = 'something else'
// "CONSTANT" is read-only
// 重新为一个常量赋值会报错
            
  • 不可只声明不赋值

const CONSTANT
// Syntax error: Unexpected token
            
  • 对象常量是可变的

const CONSTANT = {foo: 1}
CONSTANT.foo = 2
// 正常运行
            

默认参数

  • 以前

function work(name) {
  name = name || 'Bender'
  return name
}

work()
// Bender
              
  • 现在

function work(name = 'Bender') {
  return name
}
work()
// Bender
              
  • 多个参数

function work(name = 'Bender', hobby = 'drinking') {
  return `${name} likes ${hobby}`
}

work()
// Bender likes drinking
            
  • 与普通参数混合使用

function work(foo, name = 'Bender', hobby = 'drinking', bar) {
  return `${name} likes ${hobby}`
}

work()
// Bender likes drinking
              

rest 参数


function foo(...args) {
  console.log(args);
}

foo(1, 2, 3)
// [1, 2, 3]

foo()
// []
            
  • ...args 只能出现在函数参数列表的最后一个位置

// 报错
function foo(...args, name) { // Syntax error: Unexpected token
  console.log(name)
}

// 正常运行
function bar(name, ...args) {
  console.log(name)
}

bar('Bender')
// Bender
              

告别 arguments

扩展运算符


function foo(a, b, c) {
  return a + b + c
}

foo(...[1, 2, 3])
// 6
            
  • 在一个数组中扩展另一个数组

let a = [3, 4, 5]
let b = [1, 2, ...a, 6, 7]

console.log(b)
// [1, 2, 3, 4, 5, 6, 7]
              
  • 给函数传的数组元素过多?

function foo(a, b, c) {
  return a + b + c
}

foo(...[1, 2, 3, 4, 5, 6])
// 6
// 后面的被忽略了
              

解构赋值

  • 解构数组

let values = [42, 73]
let [a, b] = values

console.log(a, b)
// 42, 73
              
  • 解构对象

let bender = {name: 'Bender', hobby: 'drinking'}
let {name, hobby} = bender

console.log(name, hobby)
// Bender, drinking
              
  • 调换变量值

let a = 1, b = 2
[a, b] = [b, a]

console.log(a, b)
// 2, 1
              
  • 等式两边的数组元素位置是严格对应的

let values = [1, 2, 3]
let [, a, b, c] = values

console.log(a, b, c)
// 2, 3, undefined
              

模版字符串


let name = 'Bender'
let result = `Hello, ${name}`

console.log(result)
// Hello, Bender
            
  • 模版字符串中可以有表达式

let a = 42, b = 73
let result = `a + b is ${a + b}`

console.log(result)
// a + b is 115
              
  • 模版字符串中可以换行

let result = `Hello
 there, Bender!
`

console.log(result)
// Hello\n there, Bender!
              

带标签的模版字符串


let name = 'Bender'
let result = test `Hello,${name}!`

function test(literals, ...values) {
  console.log(literals) // ['Hello,', '!']
  console.log(values)   // ['Bender']
  return 'test'
}

console.log(result)
// test
            
  • 标签函数与模版字符串之间可以有多个空格,甚至换行符
  • 标签函数返回的值是模版字符串的最终结果

class 关键字


class Animal {
  // ...
}

let animal = new Animal()
           
  • constructor 方法

class Animal {
  constructor(name) {
    this._name = name
  }

  getName() {
    return this._name
  }
}

let animal = new Animal('kitty')
animal.getName()
// kitty
            
  • 属性

class Animal {
  constructor(name) {
    this._name = name
  }

  get name() {
    return this._name
  }

  set name(newName) {
    this._name = newName
  }
}

let animal = new Animal('kitty')
console.log(animal.name)
// kitty

animal.name = 'doge'
console.log(animal.name)
// doge

            
  • 静态方法

class Animal {
  constructor(name) {
    this._name = name
  }

  static foo() {
    return 'I am a static method'
  }
}

let animal = new Animal('kitty')

animal.foo()
// animal.foo is not a function

Animal.foo()
// I am a static method
            

类的继承


class Animal {
  constructor(name) {
    this._name = name
  }

  get name() {
    return this._name
  }

  set name(newName) {
    this._name = newName
  }

  say() {
    return 'I am an animal'
  }
}

class Cat extends Animal {
  say() {
    return 'I am a cat'
  }
}

let cat = new Cat('kitty')
cat.name
// kitty

cat.say()
// I am a cat
           
  • 在 constructor 方法中使用 super

class Cat extends Animal {
  constructor(name, pet) {
    super(name)
    this._pet = pet
  }

  get pet() {
    return this._pet
  }
}

let cat = new Cat('kitty', 'Bender')
console.log(cat.pet)
// Bender
            
  • 在普通方法中使用 super

class Cat extends Animal {
  // ...
  say() {
    return super() + '!'
  }
}

// 或者
class Cat extends Animal {
  // ...
  say() {
    return super.say() + '!'
  }
}
            

类的本质


let cat = new Cat('kitty')

cat instanceOf Cat
// true

cat instanceOf Animal
// true
            

语法糖

函数式特性

箭头函数


let add = (a, b) => a + y
            
  • 参数周围的括号

let add = (x, y) => x + y // 必需
let square = x => x * x // 不必需
let compute = () => square(add(5, 3)) // 必需

let result = compute()
// 64
            
  • 函数体的大括号

// 正常运行
let add = (x, y) => x + y
// 或者
let add = (x, y) => { return x + y }

// 返回一个对象 {a: 1}

// 失败
let test = () => { a: 1 }

// 成功
let test = () => ({ a: 1 })
// 或者
let test = () => {
  return {a: 1}
}
           

箭头函数和 this

  • 以前

function A(name) {
  this._name = name
}

A.prototype.later = function() {
  var self = this

  setTimeout(function() {
    console.log(self._name)
  }, 100)
}

var a = new A('Bender')
a.later()
// Bender
              
  • 现在

class A {
  constructor(name) {
    this._name = name
  }

  later() {
    setTimeout(() => console.log(this._name), 100)
  }

}

let a = new A('Bender')
a.later()
// Bender
            
  • callapply 不影响 this

class A {
  constructor(name) {
    this._name = name
  }
}

let a = new A('Bender')

a.foo = function() { console.log(this._name) }
a.bar = () => { console.log(this._name) }

a.foo.call({_name: 'foo'})
// foo

a.bar.call({_name: 'bar'})
// Cannot read property '_name' of undefined
            

告别 self

Symbol


let s1 = Symbol()

s1.toString()
// Symbol()
type of s1
// symbol

let s2 = Symbol('a symbol')

type of s2
// symbol
s2.toString()
// Symbol(a symbol)
            
  • 无需 new 关键字

let s = new Symbol()
// Symbol is not a constructor
              
  • 唯一性

let s1 = Symbol('a symbol')
let s2 = Symbol('a symbol')

s1 === s2
// false
              
  • 作为对象的属性

let foo = 'foo'
let bar = Symbol()

let obj = {
  foo: foo,
  [bar]: 'bar' // 注意新语法
}

console.log(obj.foo)
// foo
console.log(obj[bar])
// bar
            
  • 不可枚举

let result = []

for(let a in obj) {
  result.push(a)
}

console.log(a)
// ['foo']
            
  • 不出现在 Object.getOwnPropertyNames 中

let keys = Object.getOwnPropertyNames(obj)
console.log(keys)
// ['foo']
              
  • 可以使用 Object.getOwnPropertySymbols

let smbs = Object.getOwnPropertySymbols(obj)
console.log(obj[smbs[0]])
// bar
              

iterable

  • [Symbol.iterator] 方法的对象

let iterable = {
  index: 0,
  [Symbol.iterator]() {
    return {
      next: () => {
        if(this.index < 5) {
          this.index++
          return {done: false, value: this.index-1}
        } else {
          return {done: true, value: this.index}
        }
      }
    }
  }
}
              
  • 用于 for of 循环

for(let i of iterable) {
  console.log(i)
}
// 1, 2, 3, 4
              

iterator


let iterator = iterable[Symbol.iterator]()
iterator.next()
// {"value": 1, "done": false}
            
  • iterable 的 [Symbol.iterator] 方法返回的对象
  • next 方法会返回一个对象
    • value 是值
    • done 表明是否结束迭代

generator


let numbers = function* () {
  yield 1
  yield 2
  yield 3
}

let generator = numbers()

generator.next()
// {"value": 1, "done": false}

for(let i of generator) {
  console.log(i)
}
// 2, 3

generator.next()
// {"done": true}
              
  • 代理 yield

function* foo() {
  yield 2
  yield 3
}

function* bar() {
  yield 1
  foo() // 没有任何效果
  yield 4
}

[...bar()]
// [1, 4]

function* bar() {
  yield 1
  yield* foo()
  yield 4
}

[...bar()]
// [1, 2, 3, 4]
            
  • next 传值

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var it = foo(5);

console.log(it.next());     // { "value": 6, "done": false }
console.log(it.next(12));   // { "value": 8, "done": false }
console.log(it.next(13));   // { "value": 42, "done": true }
            

告别 callback hell

模块

export


export default foo

export let foo = 'foo'

export {foo, bar}

export {foo as bar}

export {foo as default} // 等价于 export default foo
            

import


import foo from 'foo'

import {foo, bar} from 'baz'

import {foo as bar} from 'baz'

import {default} from 'foo'

import {deafult as foo} from 'foo'

import foo, {bar, baz} from 'foo'

import * as foo from 'foo'

import 'foo'
            

特点

  • 模块是一种绑定

//------ lib.js ------
export let counter = 0;
export function inc() {
  counter++;
}

//------ main.js ------
import {inc, counter} from 'lib';
console.log(counter);
// 0
inc();
console.log(counter);
// 1
              
  • 支持循环依赖
  • 最佳实践:default export

一些新的 API

Promise


let p = new Promise((resolve, reject) => {
  // ...
})

p.then(fulfillHandler, rejectHandler)
  .catch(errorHandler)
          

链式调用


let calculate = function(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(value + 1)
    }, 0)
  })
}

calculate(1)
  .then(calculate)
  .then(calculate)
  .then(result => console.log(result))
// 4
            
  • then 会返回一个新的 promise

calculate(1)
  .then(calculate)
  .then(result => result + 1)
  .then(result => console.log(result))
// 4
            

Promise 构造函数的 API

  • Promise.resolve
  • Promise.reject
  • Promise.all(promiseList)
  • Promise.race(promiseList)

错误处理


let p = () => {
  console.log('a')
  return Promise.resolve()
}

p()
  .then(() => console.log('b'))
  .then(() => { throw new Error('error') }) // 为何要加大括号?把 throw 换成 return 如何?
  .then(() => console.log('c'), () => console.log('d'))

// a, b, d
            
  • .catch(errorHandler)

let p = () => {
  console.log('a')
  return Promise.resolve()
}

p()
  .then(() => console.log('b'))
  .then(() => { throw new Error('error') })
  .catch(() => console.log('d'))
  .catch(() => console.log('e'))
  .then(() => console.log('f'))

// a, b, d, f
            

Map


let map = new Map()

map.set('key', 42)

map.get('key')
// 42

map.has('key')
// true

map.delete('key')

map.has('key')
// false
          
  • Map 的键可以是任意类型的对象
  • Map 是 iterable 对象

Set


let set = new Set([1, 2, 2, 3])

set.size
// 3

for(let i of set) {
  console.log(i)
}
// 1, 2, 3

let o = {foo: 1}
let s = new Set()
s.add(o)
s.add(o)
s.add({foo: 1})

s.size
// 2
s.has({foo: 1})
// false
          
  • Set 中元素比较的是其引用值

数组


let a = ['a', 'b', 'c']

let values = [...a.values()]
// ['a', 'b', 'c']

let keys = [...a.keys()]
// [0, 1, 2]

let entries = [...a.entries()]
[[0,'a'],[1,'b'],[2,'c']]


Array.from(a.values())
// ['a', 'b', 'c']
          

对象


let foo = Object.assign({}, {a: 1, b: 2}, {a: 'a', c: 'c'})
// {a: 'a', b: 2, c: 'c'}


let key2 = 'second'
let bar = {
  key1: 'hello',
  [key2 + 'Key']: 'world'
}

bar.secondKey === 'world'
          

Thanks