新特性
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 注意
- let 和 const 声明的变量,在全局作用域下不会被挂在到全局对象中,浏览器为 window,node 为 global
- 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 | 写入某个属性 |
has | in 操作符 |
deleteProperty | delete 操作符 |
getProperty | Object.getPrototypeOf() |
setProperty | Object.setPrototypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.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 ⽅法 | 默认调用 |
---|---|
get | Reflect.get() |
set | Reflect.set() |
has | Reflect.has() |
deleteProperty | Reflect.delete() |
getProperty | Reflect.getPrototypeOf() |
setProperty | Reflect.setPrototypeOf() |
isExtensible | Reflect.isExtensible() |
preventExtensions | Reflect.preventExtensions() |
getOwnPropertyDescriptor | Reflect.getOwnPropertyDescriptor() |
defineProperty | Reflect.defineProperty() |
ownKeys | Reflect.ownKeys() |
apply | Reflect.apply() |
construct | Reflect.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