ECMAScript 2015 新特性

新特性

let

  • let 声明的成员只会在所声明的块中生效

如果使用 var 声明变量,可以正常输出,var 关键字有变量提升的过程

if (true) {
  var foo = 'foo'
  console.log(foo)
}

console.log(foo)

如果使用 let 声明变量,外层调用就会报错

if (true) {
  let foo = 'foo'
  console.log(foo)
}

// ReferenceError: foo is not defined
console.log(foo)
  • let 在 for 循环中的表现

如果使用 var 声明

for (var i = 0; i < 3; i++) {
  for (var i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}

// 0
// 1
// 2
// 内层结束 i = 3

如果使用 let 声明,能正常的得到想要的 9 次输出

for (let i = 0; i < 3; i++) {
  for (let i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}

// 0
// 1
// 2
// 内层结束 i = 0
// 0
// 1
// 2
// 内层结束 i = 1
// 0
// 1
// 2
// 内层结束 i = 2
  • let 应用场景:循环绑定事件,事件处理函数中获取正确索引

使用 var 作为循环声明

var elements = [{}, {}, {}]

for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}

elements[1].onclick()
// 不管调用哪一个事件,输出都是3

使用闭包来解决变量提升的问题

var elements = [{}, {}, {}]

for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}

elements[1].onclick()

使用 let 可以避免这种问题

var elements = [{}, {}, {}]

for (let i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}

elements[1].onclick()
  • for 循环中有两层作用域
for (let i = 0; i < 3; i++) {
  let i = 'foo'
  console.log(i)
}

// foo
// foo
// foo

// for 循环类似代码
let i = 0
if (i < 3) {
  let i = 'foo'
  console.log(i)
}
i++
if (i < 3) {
  let i = 'foo'
  console.log(i)
}
i++
if (i < 3) {
  let i = 'foo'
  console.log(i)
}
i++
  • let 修复了变量声明提升现象
console.log(foo)
var foo = 'foo'

console.log(foo1)
let foo1 = 'foo1'

// var 输出
// undefined

// let 输出
// ReferenceError: Cannot access 'foo1' before initialization

const

const 声明的变量不允许重新赋值

const name = 'name'

name = 'name1'

// TypeError: Assignment to constant variable.

const 声明的变量要求声明并且同时赋值

const name
name = 'name'

// SyntaxError: Missing initializer in const declaration

const 声明只是要求指向不允许被改变,但是可以修改数据内成员

const obj = {}
obj.name = 'name'
console.log(obj)
obj = {}

// { name: 'name' }
// TypeError: Assignment to constant variable.

let 和 const 注意

  1. let 和 const 声明的变量,在全局作用域下不会被挂在到全局对象中,浏览器为 window,node 为 global

  1. ReferenceError 引用错误与暂存死区

let 和 const 一致

console.log(b) // Uncaught ReferenceError: b is not defined

// 以上的区域为暂存死区
let b = 1

Array 解构

  • 获取每一样,单独定义变量
const arr = [100, 200, 300]

const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)
// 100 200 300
  • 可以使用解构的方式获取每一项的值
const arr = [100, 200, 300]

// 解构
const [foo, bar, baz] = arr
console.log(foo, bar, baz)

// 100 200 300
  • 可以只获取某一个位置的值
const arr = [100, 200, 300]

const [, , baz] = arr
console.log(baz)

// 300
  • 剩余参数
const arr = [100, 200, 300]

const [foo, ...rest] = arr
console.log(rest)

// [ 200, 300 ]
  • 如果超过了数组返回这是 undefined
const arr = [100, 200, 300]

const [foo, bar, baz, more] = arr
console.log(more)
// undefined
  • 添加默认值
const arr = [100, 200, 300]

const [foo, bar, baz = 123, more = 'default value'] = arr
console.log(baz, more)
// 300 default value
  • 应用
const path = '/foo/bar/baz'
const [, rootdir] = path.split('/')
console.log(rootdir)

// foo

object

object 解构

  • 对象解构
const obj = {name: 'sky', age: 18}
const {name} = obj
console.log(name)
// sky
  • 对象解构重命名
const obj = {name: 'sky', age: 18}
const {name: objName} = obj
console.log(objName)
// sky
  • 默认值
const obj = {name: 'sky', age: 18}
const {name: objName = 'jack', name1: objName1 = 'jack1'} = obj
console.log(objName, objName1)
// sky jack1
  • 应用
const {log} = console
log('foo')
log('bar')
log('123')
// foo
// bar
// 123

对象字面量

const bar = '345'

const obj = {
  foo: 123,

  // bar: bar
  // 属性名与变量名相同,可以省略 : bar
  bar,

  // 方法可以省略 : funtion
  method1: function () {
    consle.log('method1')
    // 这种方法就是普通的函数,同样影响 this 指向,指向当前对象
    console.log(this, 'method1')
  },

  method2() {
    console.log('method2')
    // 这种方法就是普通的函数,同样影响 this 指向,指向当前对象
    conosle.log(this, 'method2')
  },

  // 123: 12 不允许
  // 允许键是一个变量,但是只能是字符串
  [bar]: 123,
}

obj.method1()
obj.method2()
console.log(obj)

Object.assign 方法

Object.assign(target, …args)
将 target 后面多个对象合并到 target 中,相同的属性将会被覆盖

const source1 = {
  a: 123,
  b: 123,
}

const source2 = {
  b: 789,
  c: 789
}

const target = {
  a: 456,
  c: 456
}

const result = Object.assign(target, source1, source2)
console.log(result)
console.log(result === target)
// { a: 123, c: 789, b: 789 }
// true

应用:

function fun(obj) {
  // 这种方法会更改外面传入的对象
  // obj.name = 'func obj'
  // console.log(obj)

  // 达到复制对象的目的
  const funcObj = Object.assign({}, obj)
  funcObj.name = 'func obj'
  console.log(funcObj)
}

const obj = {name: 'global obj'}
func(obj)
console.log(obj)
// { name: 'func obj' }
// { name: 'global obj' }

Object.is

Object.is 可以判断 +0 和 -0 NaN

console.log(0 == false)
console.log(0 === false)
console.log(+0 === -0)
console.log(NaN === NaN)
console.log(Object.is(+0, -0))
console.log(Object.is(NaN, NaN))

// true
// false
// true
// false
// false
// true

string

模版字符串

const str = `hello es2015`
console.log(str)
// hello es2015

// 允许换行
const str1 = `
hello es2015,
hello
this is a `string`
`
console.log(str1)
// hello es2015,
// hello
// this is a `string`

const name = 'tom'
// 可以通过 ${} 插入表达式,表达式执行的结果将会输出到对应的位置
const msg = `hey, ${name} --- ${1 + 3} --- ${Math.random()}`
console.log(msg)
// hey, tom --- 4 --- 0.7121610722271636

带标签的模版字符串

模版字符串的标签就是一个特殊的函数,使用这个标签就是调用这个函数

// const str = console.log`hello world`

const name = 'tom'
const gender = false

function myTagFunc(strings, name, gender) {
  // strings 是以 ${} 为间隔分割字符串的值,可以自定义处理字符串的返回
  console.log(strings, name, gender)
  const sex = gender ? 'man' : 'woman'
  return strings[0] + name + strings[1] + sex + strings[2]
}

const result = myTagFunc`hey, ${name} is a ${gender}`
console.log(result)
// [ 'hey, ', ' is a ', '' ] tom false
// hey, tom is a woman

扩展方法

  • includes
const message = 'Error: foo is not defined.'
console.log(message.startsWith('Error'))
console.log(message.endsWith('.'))
console.log(message.includes('foo'))
// true
// true
// true

Function

默认值

function foo(enable) {
  enable = enable === undefined ? true : false
  console.log('foo invoked - enable: ', enable)
}

foo()
foo(true)
// foo invoked - enable:  true
// foo invoked - enable:  false

// 默认值参数一定要在形参列表的最后
function foo1(enable = true) {
  console.log('foo invoked - enable: ', enable)
}

foo1()
foo1(false)
// foo invoked - enable:  true
// foo invoked - enable:  false

剩余参数

function foo() {
  console.log(arguments)
}

function foo1(first, ...args) {
  console.log(first, args)
}

foo(1, 2, 3)
foo1(1, 2, 3, 4)
// [arguments] { '0': 1, '1': 2, '2': 3 }
// 1 [ 2, 3, 4 ]

展开数组参数

const arr = [1, 2, 3]
console.log(arr[0], arr[1], arr[2])
console.log.apply(console, arr)
console.log(...arr)
// 1 2 3
// 1 2 3
// 1 2 3

箭头函数

function inc(num) {
  return num + 1
}

const inc1 = n => n + 1

console.log(inc(1))
console.log(inc1(2))
// 2
// 3

// 完成参数列表,函数体多条语句,返回值仍需 return

const add = (n1, n2) => {
  console.log(n1, n2)
  return n1 + n2
}

console.log(add(1, 3))
// 1 3
// 4

// 应用

const arr = [1, 2, 3, 4, 5]
console.log(arr.filter(i => i % 2))
// [ 1, 3, 5 ]

箭头函数与 this

const person = {
  name: 'tom',

  sayHi: function () {
    console.log('hi, my name is ', this.name)
  },

  sayHi1: () => {
    console.log('hi, my name is ', this.name)
  },

  sayHiAsync: function () {
    const _this = this
    setTimeout(function () {
      console.log(this.name, 'setTimeout')
      console.log(_this.name, 'setTimeout1')
    }, 1000)

    console.log(this.name, 'sayHiAsync')

    setTimeout(() => {
      console.log(this.name, 'setTimeout2')
    }, 1000)
  }
}

// sayHi 是由 person 调用,而且不是箭头函数,所以 this 指向了调用者
person.sayHi()
// hi, my name is  tom

// sayHi1 是由 person 调用,但是是箭头函数,this 指向了父级作用域,则是 window,window 中没有 name 属性,所以打印 undefined
person.sayHi1()
// hi, my name is  undefined

// 第一个 setTimeout 中的 this,由于不是箭头函数,这个回调函数是在 setTimeout 中执行,则 this 指向了 Timeout 对象,这里可以理解为 setTimeout 中是由 Timeout 对象调用了 callback 函数
// 第二个 this,指向了 sayHiAsync 调用者,即 person
// 第三个 setTimeout 中的 this,由于是箭头函数,则 this 指向上一层作用域,则也是 person 对象
person.sayHiAsync()
// tom sayHiAsync
// undefined setTimeout
// tom setTimeout1
// tom setTimeout2

Proxy 对象

const p = new Proxy(target, handler)

target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理) handler:
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

handler ⽅法触发方法
get读取某个属性
set写入某个属性
hasin 操作符
deletePropertydelete 操作符
getPropertyObject.getPrototypeOf()
setPropertyObject.setPrototypeOf()
isExtensibleObject.isExtensible()
preventExtensionsObject.preventExtensions()
getOwnPropertyDescriptorObject.getOwnPropertyDescriptor()
definePropertyObject.defineProperty()
ownKeysObject.keys(), Object.getOwnPropertyNames(), Object.getOwnPropertySymbols()
apply调用一个函数
construct用 new 调用一个函数
const person = {
  name: 'name',
  age: 20
}

const personProxy = new Proxy(person, {
  // 监视属性读取
  get(target, property) {
    console.log(target, property)
    return property in target ? target[property] : 'default'
  },

  // 监听属性设置 
  set(target, property, value) {
    if (property === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${value} is not an int number`)
      }
    }

    target[property] = value
  },
})

console.log(personProxy.name)
personProxy.age = 30
console.log(personProxy)
console.log(person)
// { name: 'name', age: 20 } name
// name
// { name: 'name', age: 30 }
// { name: 'name', age: 30 }

对比 Proxy 与 Object.defineProperty()

  • 优势1:Proxy 可以监听读写以外的操作
const person = {
  name: 'name',
  age: 10
}

const personProxy = new Proxy(person, {
  deleteProperty(target, property) {
    console.log('delete', property)
    delete target[property]
  }
})

delete personProxy.age
console.log(person)
// delete age
// { name: 'name' }
  • 优势2: Proxy 可以很方便的监听数组操作
const list = []

const listProxy = new Proxy(list, {
  // 数组也可以理解为一个对象,下标就是对应键,还有一个 length 属性的键
  // {
  //   0: 1
  //   1: 2
  //   length: 2
  // }
  set(target, property, value) {
    console.log('set', property, value)
    target[property] = value
    // 表示设置成功,否则会报错
    return true
  }
})

listProxy.push(1)
listProxy.push(2)
// set 0 1
// set length 1
// set 1 2
// set length 2
  • 优势3: Proxy 不需要侵入对象

Object.defineProperty 用法

const person = {}

Object.defineProperty(person, 'name', {
  get() {
    console.log('get name')
    return person._name
  },
  set(value) {
    console.log('set name')
    person._name = value
  }
})

Object.defineProperty(person, 'age', {
  get() {
    console.log('get age')
    return person._age
  },
  set(value) {
    console.log('set age')
    person._age = value
  }
})

person.name = 'name'
person.age = 20
console.log(person)
// set name
// set age
// { _name: 'name', _age: 20 }

Proxy 用法

const person = {}
const personProxy = new Proxy(person, {
  get(target, property) {
    console.log('get', property)
    return target[property]
  },
  set(target, property, value) {
    console.log('set', property, value)
    target[property] = value
  }
})

personProxy.name = 'name'
personProxy.age = 18
console.log(person)
// set name name
// set age 18
// { name: 'name', age: 18 }

Reflect 对象

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handlers 的方法相同。
Reflect不是一个函数对象,因此它是不可构造的。 Reflect 对象提供了以下静态方法,这些方法与proxy handler methods
的命名相同.

Proxy handler ⽅法默认调用
getReflect.get()
setReflect.set()
hasReflect.has()
deletePropertyReflect.delete()
getPropertyReflect.getPrototypeOf()
setPropertyReflect.setPrototypeOf()
isExtensibleReflect.isExtensible()
preventExtensionsReflect.preventExtensions()
getOwnPropertyDescriptorReflect.getOwnPropertyDescriptor()
definePropertyReflect.defineProperty()
ownKeysReflect.ownKeys()
applyReflect.apply()
constructReflect.construct()
  • 结合 Proxy 使用
const obj = {
  foo: '123',
  bar: '456',
}

const proxy = new Proxy(obj, {
  get(target, property) {
    console.log('watch logic ~')
    // Proxy 默认实现
    return Reflect.get(target, property)
  }
})

console.log(proxy)
console.log(proxy.foo)
// { foo: '123', bar: '456' }
// watch logic ~
// 123
  • 独立使用
const obj = {
  name: 'name',
  age: 18
}

console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
// true
// true
// [ 'name' ]

class

新增 class 关键字来定义类

class Person {
  constructor(name) {
    this.name = name
  }

  say() {
    console.log('hi, my name is', this.name)
  }
}

const p = new Person('tom')
p.say()

// hi, my name is tom

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 p = Person.create('tom')
p.say()
// hi, my name is tom

extends 类继承

class Person {
  constructor(name) {
    this.name = name
  }

  say() {
    console.log('hi, my name is', this.name)
  }
}

// 通过 extends 继承 Person 的属性和方法
class Student extends Person {
  constructor(name, number) {
    // 通过 super 调用父类的构造方法
    super(name)
    this.number = number
  }

  hello() {
    // 通过 super 调用父类的 方法
    super.say()
    console.log('my school number is', this.number)
  }
}

const s = new Student('jack', 100)
s.hello()
// hi, my name is jack
// my school number is 100

Set

  • 基本用法

Set 数据结构,保存唯一的数据,对象类型无法去重相同的值,对象类型是根据地址判断是否重复

const s = new Set()

s.add(1).add(2).add(3).add(4).add(2)
console.log(s)
// Set(3) { 1, 2, 3 }

// 遍历
s.forEach(i => console.log(i))
// 1
// 2
// 3
// 4
for (let i of s) {
  console.log(i)
}
// 1
// 2
// 3
// 4

console.log(s.size)
// 输出 set 中的长度 4

console.log(s.has(100))
console.log(s.has(2))
// 判断是否有值
// false
// true

console.log(s.delete(2))
// 删除一个值,成功返回 true
console.log(s)
// Set(3) { 1, 3, 4 }

// 清空 set
s.clear()
console.log(s)
//  Set(0) {}

  • 使用场景

数组去重

const arr = [1, 2, 1, 3, 4, 1]
console.log(Array.from(new Set(arr)))
console.log([...new Set(arr)])

Map

  • Map 和 Object 区别
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{a: 1}] = 'value1'
obj[{a: 2}] = 'value2'
console.log(obj)
console.log(Object.keys(obj))

// 对象类型会把所有的 key 变为 字符串
// { '123': 'value', true: 'value', '[object Object]': 'value2' }
// [ '123', 'true', '[object Object]' ]
  • Map 可以设置任意类型的 key,不会被转为 字符串
const m = new Map()

const tom = {name: 'tom'}
m.set(tom, 90)
m.set(tom, 91)
m.set({name: 'tom'}, 92)
console.log(m)
// Map(2) { { name: 'tom' } => 91, { name: 'tom' } => 92 }

console.log(m.get(tom))
// 91

console.log(m.get({name: 'tom'}))
// undefined

console.log(m.has(tom))
// true

console.log(m.delete(tom))
// true

console.log(m.clear())
// undefined

console.log(m)
// Map(0) {}

m.set(1, 1)
m.set(true, true)
m.set('key', 'value')
// Map(3) { 1 => 1, true => true, 'key' => 'value' }

// Map 遍历
m.forEach((value, key) =>{
  console.log(value, key)
})
// 1 1
// true true
// value key

Symbol

Symbol 是一个基本类型,创建出来的对象永远不会相等

const s = Symbol()

console.log(s)
console.log(typeof s)

// Symbol()
// symbol

console.log(Symbol() === Symbol())
// false

// 添加文字描述
console.log(Symbol('foo'))
console.log(Symbol('bar'))
console.log(Symbol('baz'))

// Symbol(foo)
// Symbol(bar)
// Symbol(baz)

// 使用 Symbol 为对象添加不重复的键

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

// 使用计算属性名添加

const obj = {
  [Symbol()]: 123
}

console.log(obj)
// { [Symbol()]: 123 }
  • 案例 Symbol 模拟实现私有成员
// a.js ===================
const name = Symbol()
const person  = {
  [name]: 'name',
  say () {
    console.log(this[name])
  }
}

// 只对外暴露 person
// b.js ==================

/**
 * 由于无法创建出一样的 Symbol 值
 * 所以无法直接访问到 person 中的 [私有] 成员
 * person[Symbol()]
 */
person.say()
  • Symbol 补充
console.log(Symbol() === Symbol())
console.log(Symbol('foo') === Symbol('foo'))
// false
// false
  • Symbol 全局注册表

可以根据 Symbol.for 注册一个 Symbol 对象,这样通过 Symbol.for 得到的对象就是同一个对象

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2)
console.log(Symbol.for(true) === Symbol.for(true))
// true
// true
  • Symbol 内置常量
console.log(Symbol.iterator)
// Symbol(Symbol.iterator)

console.log(Symbol.hasInstance)
// Symbol(Symbol.hasInstance)

const obj = {
  [Symbol.toStringTag]: 'xObject'
}
console.log(obj.toString())
// [object xObject]
  • Symbol 属性名获取

使用 for JSON.stringify 或者 Object.keys 都无法获取到 Symbol 的 key 和 value,
Symbol 的 key 只能通过 Object.getOwnPropertySymbols() 获取

const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}

for( const key in obj) {
  console.log(key)
}
// foo

console.log(Object.keys(obj))
// [ 'foo' ]

console.log(JSON.stringify(obj))
// {"foo":"normal value"}

console.log(Object.getOwnPropertySymbols(obj))
// [ Symbol() ]

for of 循环

const arr = [100, 200, 300, 400]
for (const item of arr) {
  console.log(item)
}
// 100
// 200
// 300
// 400

// for ... of  循环可以替代数组对象的 forEach 方法
arr.forEach(item => {
  console.log(item)
})
// 100
// 200
// 300
// 400

// forEach 无法跳出循环,必须使用 some 或者 every 方法
// for ... of 可以使用 break

for(const item of arr) {
  console.log(item)
  if (item > 100) {
    break
  }
}
// 100
// 200

// forEach 不能跳出循环
arr.forEach()
arr.some()
arr.every()

// 遍历 set 和遍历数组相同
const s = new Set(['foo', 'bar', 'foo'])
for (const item of s) {
  console.log(item)
}
// foo
// bar

// 遍历 Map 可以配合数组解构语法,直接获取键值对
const m = new Map()
m.set('foo', '123')
m.set('bar', '345')
for(const [key, value] of m) {
  console.log(key, value)
}
// foo 123
// bar 345

// 普通对象不能直接被 for ... of 遍历
const obj = {foo: 123, bar: 456}
for (const item of obj) {
  console.log(item)
}
// for(const item of obj) {
// ^
//
// TypeError: obj is not iterable

Iterator 迭代器

const set = new Set(['foo', 'bar', 'baz'])

// 获取 Set 中的迭代器
const iterator = set[Symbol.iterator]()
while(true) {
  // 获取当前元素
  const current = iterator.next()
  // 判断是否遍历完成
  if (current.done) {
    break
  }
  // 打印结果
  console.log(current.value)
}
// foo
// bar
// baz

Iterable 实现可迭代接口

实现 Iterable 接口,需要在对象中新增 Symbol.iterator 属性

const obj = {
  // 新增 Symbol.iterator key,返回 next 对象
  [Symbol.iterator]: function () {
    return {
      next: function () {
        return {
          // next 返回值 包含 value 和 done
          value: 'name',
          done: true
        }
      }
    }
  }
}

console.log(obj)
  • 案例
const obj = {
  store: ['foo', 'bar', 'baz'],
  // 实现 Iterable 接口
  [Symbol.iterator]: function () {
    let index = 0
    // 这里的 this 指向 当前对象
    const self = this
    return {
      // 返回 next()
      next: function () {
        // 这里的 this 指向 Symbol.iterator 中 return 的对象
        const result = {
          // next() 返回 value 和 done
          value: self.store[index],
          done: index >= self.store.length
        }
        index++
        return result
      }
    }
  }
}

for (const item of obj) {
  console.log(item)
}
// foo
// bar
// baz

Iterator-pattern 迭代器模式

// 场景:遍历多个任务清单
const todo = {
  life: ['eating', 'sleeping', 'hitting Bean'],
  learn: ['chinese', 'math', 'english'],
  work: ['tea'],

  // 提供一个统一遍历的方法
  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
        }
      }
    }
  }
}

todo.each(i => console.log(i))

for(const item of todo) {
  console.log(item)
}

Generator

function * foo () {
  console.log('111')
  yield 100
  console.log('222')
  yield 200
  console.log('333')
  yield 300
}

const generator = foo()

// 第一次调用, 函数题开始执行,遇到第一个 yield 暂停
console.log(generator.next())
// 第二次调用,从暂停位置继续,知道遇到下一个 yield 再次暂停
console.log(generator.next())
// 。。。
console.log(generator.next())
// 第四次调用,已经没有需要执行的内容,所以 value 得到了 undefined
console.log(generator.next())
// { value: 100, done: false }
// 222
// { value: 200, done: false }
// 333
// { value: 300, done: false }
// { value: undefined, done: true }
  • 案例一:发号器
function* createId() {
  let id = 1
  while (true) {
    yield id++
  }
}

const idMaker = createId()

console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
// 1
// 2
// 3
// 4
// 5
  • 案例二:使用 generator 实现 iterator 方法
const todo = {
  life: ['eating', 'sleeping', 'hitting Bean'],
  learn: ['chinese', 'math', 'english'],
  work: ['tea'],

  // Generator 正好返回 value 和 done 类型的值,正好符合迭代器
  [Symbol.iterator]: function* () {
    const all = [...this.life, ...this.learn, ...this.work]
    for (const item of all) {
      yield item
    }
  }
}

for (const item of todo) {
  console.log(item)
}
// sleeping
// hitting Bean
// chinese
// math
// english
// tea
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>