共计 5345 个字符,预计需要花费 14 分钟才能阅读完成。
ES2015 减少的这两个个性之前始终没有很明确,或者是说看完教程就很容易遗记,这次通过写一篇博文来加强了解与记忆
Iterator
Iterator 中文名为迭代器,顾名思义是用来循环遍历的,咱们平时说到循环遍历必定会想到 for、while 循环以及数组的内建办法。for、while 循环就不多说了,这是语言中最根底的循环迭代办法,毛病是对于很简略的迭代须要写一些模板代码,不够易用。所以对于那些罕用的须要遍历的数据类型比方 Array,JS 提供了 forEach,map,filter 等不便且附带特定性能的办法。当然这些办法都属于数组,对于另外的咱们想遍历的数据结构比方字符串就没方法应用了,于是 ES2015 又退出了 for…of 循环,能够用来遍历任意的 可迭代对象。
那么问题来了,这个 可迭代对象 是什么呢?这就波及到 ES2015 退出的新概念,可迭代协定 (iterable protocol)与 迭代器协定(iterator protocol)。
-
可迭代协定
可迭代协定规定 可迭代对象 必须实现一个叫
Symbol.iterator
的办法,该办法必须返回一个 迭代器对象。下面的问题就有了答案,只有一个对象自身或者原型链中具备这个办法,那么这个对象就是可迭代的。// 这样的对象就是一个可迭代对象 const iterableObj = { // 必须有该办法 [Symbol.iterator]() {return <iterator object>; // 该办法必须返回迭代器对象} };
迭代器对象 又是什么呢?这就是迭代器协定的内容了。
-
迭代器协定
迭代器协定规定 迭代器对象 必须有一个
next
办法,该办法必须返回一个具备done
与value
属性的对象。这样说比拟形象,以下代码示例能够形象的展现迭代器对象:// 这样的对象就是迭代器对象 const itratorObj = {next() { return { done: false, // 当该值为 ture 时迭代完结 value: null, // 每次迭代返回的值,当 done 为 true 时能够省略 }; } };
弄清楚了下面两个基本概念后,咱们就了解了何为 可迭代对象。有些内置对象本人实现了可迭代协定,所以他们是可迭代的(能够用 for…of 遍历),比方 Array,String,Set,Map 等。当然咱们本人定义的对象也能够是可迭代的,只有咱们本人实现可迭代协定。上面就举一个例子。
const range = {
from: 1,
to: 5,
};
咱们有以上对象,如果当初间接应用 for…of 循环去遍历,那必定是报错的,错误信息为 range 不可迭代。咱们想让他变的可迭代并且依照咱们本人想要的形式迭代:从 from 迭代到 to,仅须要实现可迭代协定即可。
const range = {
form: 1,
to: 5,
[Symbol.iterator]() {
// 如何实现?return {next() {
// 上面的两个属性什么时候变动?return {
done: false,
value: 1,
};
}
};
}
};
说起来笨重,然而这个代码要怎么实现呢?这就波及到 Symbol.iterator
办法的运行机制了。
当一个对象进行迭代的时候,在迭代之前会先执行 Symbol.iterator
办法,而后应用该办法返回的 迭代器对象 来进行迭代管制。艰深来说就是,每次迭代的时候都执行一次 next
办法,把 value
值作为该次迭代的返回值,当 done
为 true 时迭代完结。顺着这个思路就能够实现咱们的自定义迭代办法了。
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let cur = this.from; // 迭代前初始化一个以后迭代值
const to = this.to; // 暂存终止值
return {next() {
const done = cur > to; // 当迭代值大于 to 的时候迭代完结
return {
done,
value: done ? undefined : cur++, // 将迭代值返回
};
}
};
}
};
// 验证
for (let item of range) {
// 这里须要留神:for...of 此类的迭代操作会疏忽 done 为 true 的值
console.log(item); // 1 2 3 4 5
}
一般来说,举荐对象既实现可迭代协定,也实现迭代器协定,这样被称为格局良好的可迭代对象,就像这样:
const range = {
from: 1,
to: 5,
// 对象具备 next 办法,合乎迭代器协定
next() {
const done = this._iteratorVal > this.to;
return {
done,
value: done ? undefined : this._iteratorVal++,
};
},
[Symbol.iterator]() {
this._iteratorVal = this.from;
return this; // 可迭代协定返回对象自身
}
};
咱们能够实现一个用于生成可迭代对象 forEach 的通用办法,更不便的去迭代非数组的可迭代对象。
function iterableForEachFactory(obj) {return function (callback) {
let index = 0;
for (let item of obj) {callback(item, index);
index++;
}
}
}
const rangeForEach = iterableForEachFactory(range);
rangeForEach((item, index) => console.log(item, index)); // 1 2 3 4 5, 0 1 2 3 4
到目前为止,咱们只在对象中应用迭代器,有时候咱们仅仅只须要一个迭代器去进行自定义的迭代逻辑,相似于 for、while 循环,然而 for while 循环是主动的,一旦开始迭代就无奈暂停,只能彻底跳出循环。利用迭代器协定,咱们能实现一个能够暂停的手动循环,并且能自定义循环逻辑。
function iteratorMaker(param) {
let index = 0;
let lastValue = null;
// param 能够是代表迭代次数的数字,也能够是自定义迭代逻辑的函数,也能够不传
const isFunction = typeof param === 'function';
const isNumber = typeof param === 'number';
return {next() {
const nextIndex = index++;
lastValue = isFunction ? param(lastValue, nextIndex) : nextIndex;
// 如果传入了自定义迭代函数,那么返回 undefined 能够终止循环
const done = isFunction ?
lastValue === undefined :
isNumber ? nextIndex >= param : false;
return {
done,
value: done ? undefined : lastValue,
};
}
};
}
// 循环两次的一般迭代器
const iteratorCircle2 = iteratorMaker(2);
iteratorCircle2.next().value; // 0
iteratorCircle2.next().value; // 1
iteratorCircle2.next().value; // undefined
// 每次翻倍的无穷迭代器
const iteratorDoubleCircle = iteratorMaker(doubleCircle);
function doubleCircle(lastValue) {return lastValue ? lastValue * 2 : 1;}
iteratorDoubleCircle.next().value; // 1
iteratorDoubleCircle.next().value; // 2
iteratorDoubleCircle.next().value; // 4
iteratorDoubleCircle.next().value; // 8
Generator
前文具体阐明了 Iterator 的作用与用法,接下来了解并应用 Generator(生成器)就非常简单了。能够这么简略粗犷的了解:Generator 就是 Iterator 的语法糖,然而它有更高级的性能。他们的关系很容易让人想到 Proxy 与 Reflect,Reflect 能够完满配合 Proxy 的语法,它也有本人额定的性能。
-
语法
function* gen() { yield 1; yield 2; yield 3; return; // return 是可选的,相当于 done: true } // 这个就是 generator 对象,它既实现了可迭代协定,也实现了迭代器协定 const g = gen(); typeof g[Symbol.iterator]; // function typeof g.next; // function g[Symbol.iterator]() === g; // true
看到这里是不是很眼生,没错,generator 对象和咱们之前实现的格局良好的可迭代对象 range 是很相似的。
咱们能够很容易的将 range 的例子应用 generator 重写一下:
const range = { from: 1, to: 5, *[Symbol.iterator]() {for (let i = this.from; i <= this.to; i++) {yield i;} } }; // 验证 for (let item of range) {console.log(item); // 1 2 3 4 5 }
-
组合
Generator 能够嵌套进行组合,来实现各种各样的性能。
function* generateSequence(start, end) {for (let i = start; i <= end; i++) yield i; } function* generatePasswordCodes() { // 0..9 yield* generateSequence(48, 57); // 组合的语法为 yield* 前面跟任意 generator 函数 // A..Z yield* generateSequence(65, 90); // a..z yield* generateSequence(97, 122); } let str = ''; for(let code of generatePasswordCodes()) {str += String.fromCharCode(code); } console.log(str); // 0..9A..Za..z
也能够应用组合递归来实现数组打平
const arr = [1, 2, 3, [4, 5, [6, 7, 8, [9]]]]; function* flatten(arr) {for (let item of arr) {if (Array.isArray(item)) {yield* flatten(item); } else {yield item;} } } const flatArr = [...flatten(arr)]; console.log(flatArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
-
双向 yield
仔细的读者会发现,到目前为止,咱们应用 generator 是实现不了上文中
iteratorMaker
函数的,这里咱们就须要介绍 generator 的一个最弱小的概念:双向 yield。yield
给人的感觉是与 iterator 中的value
一样,向内部迭代输入值,然而它还能够接管内部next
函数传入的值,作为下一次执行的输入后果。function* gen() { const result = yield 1; // 将 yield 表达式赋值给一个变量就实现了双向 yield yield result * 2; // 下一次 next 函数传入的值会赋值给 result } const g = gen(); g.next().value; // 1 g.next(2).value; // 4
理解这个个性之后,咱们就能够对
iteratorMaker
函数用 generator 重写了:function iteratorMaker(param) { let index = 0; let lastValue = null; const isFunction = typeof param === 'function'; const isNumber = typeof param === 'number'; function* gen() { let passValue = null; while (isNumber ? index <= param : lastValue !== undefined) {passValue = yield (passValue === null ? lastValue : initial); } } const g = gen(); return {next() { const nextIndex = index++; lastValue = isFunction ? param(lastValue, nextIndex) : nextIndex; return g.next(lastValue); } }; }
总的来说,Iterator 与 Generator 都是 ES2015 外面比拟少用的个性,也有肯定的复杂度,特地是 Generator 的双向 yield,须要多入手写一写实例能力比拟好的了解它,心愿这篇文章能对你有用。