使用 generator 给普通对象添加 iterator 接口

  • Post author:
  • Post category:其他




给普通对象添加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])
}
输出顺序是什么? 欢迎留言讨论



版权声明:本文为hbjiankely原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。