ES6学习——迭代器(Iterators):迭代器接口高级应用

先看个概念叫iterator is iterable,就是迭代器本身可迭代。JS原生对象的迭代器基本都是这样的,看个例子:

var arr=[1,2,3],iter = arr[Symbol.iterator]();
for(let i of iter){
	console.log(i);
}

如果我们自己写的迭代器,怎么实现呢?其实很简单,让Symbol.iterator方法返回this,然后把next方法拿出来:

function iterateOver(...args) {
	let index = 0;
	let iterable = {
		[Symbol.iterator]() {
			return this;//返回this
		},
		next() {//把next放到外面的对象上
			if (index < args.length) {
				return { value: args[index++],done:false };
			} else {
				return { done: true };
			}
		}
	};
	return iterable;
}

var it = iterateOver(1,2,3)[Symbol.iterator]();
for(let i of it){
	console.log(i);
}

for(let i of iterateOver(1,2,3)){
    console.log(i);
}


然后我们在看个问题,就是next返回值,规范中明确说明了如果返回值不是Object,会抛出TypeError,在7.4.2中:

The abstract operation IteratorNext with argument iterator and optional argument value performs the following steps:
1. If value was not passed, then
    a. Let result be Invoke(iterator, "next", « »).
2. Else,
    a. Let result be Invoke(iterator, "next", «value»).
3. ReturnIfAbrupt(result).
4. If Type(result) is not Object, throw a TypeError exception.
5. Return result.


如果你返回null,同样会报错,虽然null用typeof出来是object。如果你返回的是new String,或者new Number等对象类型,是不会报错的,但更糟糕的是会无限循环。因为你的done属性被认为一直是false,这里一定要小心。看规范中如何判断迭代器完成的:

IteratorComplete ( iterResult )
The abstract operation IteratorComplete with argument iterResult performs the following steps:
1. Assert: Type(iterResult) is Object.
2. Return ToBoolean(Get(iterResult, "done")).


接着我们看一个有意思的扩展,JS的Number类型上是没有定义迭代器的,那么可不可以扩展一下,让Number变成可迭代的,YES!

if (!Number.prototype[Symbol.iterator]) {
	Object.defineProperty(Number.prototype,Symbol.iterator,{
		writable: true,
		configurable: true,
		enumerable: false,
		value: function iterator(){
			var i, inc, done = false, top = +this;
			inc = 1 * (top < 0 ? -1 : 1);//判断正负值,确定迭代方向
			return {
				[Symbol.iterator](){ return this; },//iterator is iterable
				next() {
					if (!done) {
						if (i == null) {//初始设置0
							i = 0;
						}
						else if (top >= 0) {//大于0的数,则从0到number
							i = Math.min(top,i + inc);
						}
						else {//小于0的数,则从0到-number
							i = Math.max(top,i + inc);
						}
						if (i == top) done = true;
						
						return { value: i, done: false };
					}
					else {
						return { done: true };
					}
				}
			};
		}
	});
}
然后我们就可以直接迭代数值类型了:

for(let i of 3){
    console.log(i)//0 1 2 3
}

[...3]//[0,1,2,3]
[...-3]//[0,-1,-2,-3]


最后我们看几个迭代器的工具函数:

1)原来见过的,把Object变成可迭代的

function objectEntries(obj) {
	let index = 0;
	let propKeys = Object.getOwnPropertyNames(obj);//忽略Symbol类型的属性
	return {
		[Symbol.iterator]() {
			return this;
		},
		next() {
			if (index < propKeys.length) {
				let key = propKeys[index];
				index++;
				return { value: [key, obj[key]] };
			} else {
				return { done: true };
			}
		}
	};
}


let obj = { first: 'Jane', last: 'Doe' };
for (let [key,value] of objectEntries(obj)) {
	console.log(`${key}: ${value}`);
}


2)根据参数,只返回迭代器的前几项

function take(n, iterable) {
	let iter = iterable[Symbol.iterator]();
	return {
		[Symbol.iterator]() {
			return this;
		},
		next() {
			if (n > 0) {
				n--;
				return iter.next();
			} else {
				return { done: true };
			}
		}
	};
}

let arr = ['a', 'b', 'c', 'd'];
for (let x of take(2, arr)) {
	console.log(x);
}
上面这个方法,还可以处理无限循环的迭代器。


3)把多个迭代器转成多维数组形式

function zip(...iterables) {
	let iterators = iterables.map(i => i[Symbol.iterator]());
	let done = false;
	return {
		[Symbol.iterator]() {
			return this;
		},
		next() {
			if (!done) {
				let items = iterators.map(i => i.next());
				done = items.some(item => item.done);
				if (!done) {
					return { value: items.map(i => i.value) };
				}
			}
			return { done: true };
		}
	}
}

let zipped = zip(['a', 'b', 'c'], ['d', 'e', 'f', 'g']);
for (let x of zipped) {
	console.log(x);
}
//["a", "d"] ["b", "e"] ["c", "f"]


*以上全部代码在Chrome 48下通过测试


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值