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

给普通对象添加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表达式(helloworld),即该函数有三个状态: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])
}
输出顺序是什么? 欢迎留言讨论

你可能感兴趣的:(ES6)