给普通对象添加iterator接口,使其能够被for…of循环
原生具备 iterator 接口的数据结构如下
- Array
- Map
- Set
- String
- 函数的 arguments 对象
- NodeList 对象
- TypedArray
对象不具备iterator 接口,不能被for…of 循环
let obj = {
a: 1,
b: 2
}
for(let x of obj) {
console.log(x)
}
// 报错 obj is not iterable
了解 generator 的含义
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,
function
关键字与函数名之间有一个星号;二是,函数体内部使用
yield
表达式,定义不同的内部状态(
yield
在英语里的意思就是“产出”)。
举个例子
function* hiGenerator() {
yield 'hello';
yield 'world'
return 'end'
}
引用阮一峰的解释,浅显易懂
上面代码定义了一个 Generator 函数
helloWorldGenerator
,它内部有两个
yield
表达式(
hello
和
world
),即该函数有三个状态:hello,world 和 return 语句(结束执行)。
然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
下一步,必须调用遍历器对象的
next
方法,使得指针移向下一个状态。也就是说,每次调用
next
方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个
yield
表达式(或
return
语句)为止。换言之,Generator 函数是分段执行的,
yield
表达式是暂停执行的标记,而
next
方法可以恢复执行。
划重点:generator 是一个函数,执行这个函数的返回值是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)
let hg = hiGenerator();
hg.next();
// {value: 'hello', done: false}
hg.next();
// {value: 'world', done: false}
hg.next();
// {value: 'end', done: true}
hg.next();
// {value: undefined, done: true}
使用generator 生成 iterator 接口
function* myIterator(o) {
// 使用for...in循环不能保证顺序输出
for(let x in o) {
yield o[x];
}
}
// 推荐另一种写法
function* myIterator(o) {
let arr = Object.keys(o);
for(let x of arr) {
yield o[x]
}
}
for(let x of myIterator(obj)) {
console.log(x);
}
// 1
// 2
// 进阶版,同时获取key
function* myIterator(o) {
let arr = Object.keys(o);
for(let x of arr) {
yield [x, o[x]]
}
}
for(let [key, value] of myIterator(obj)) {
console.log(`${key}: ${value}`)
}
// a: 1
// b: 2
给对象直接添加 iterator 接口, 不使用generator函数
obj[Symbol.iterator] = function() {
return {
next: function() {
let arr = Object.keys(this);
if (this.index < arr.length) {
this.index++;
return {value: this[arr[this.index]], done: false}
} else {
return {value: undefined, done: true}
}
},
index: -1
}
}
for(let x of obj) {
console.log(x);
}
// 1
// 2
// undefined
for…in 循环
let obj = {
c: 3,
a: 1,
b: 2
}
for (let x in obj) {
console.log(obj[x])
}
输出顺序是什么? 欢迎留言讨论