不推荐Iterator方法。 Iterator 函数是一个 SpiderMonkey 专有特性,而且会在某一时刻被删除。
有一点,须要清楚的,就是“迭代协议”。迭代协议MDN说明node
// 简单示例,摘自“深刻理解ES6” function createIterator(items) { let i = 0; return { next: function() { let done = (i >= items.length); let value = !done ? items[i++] : undefined; return { done, value } } } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // { done: false, value: 1 } console.log(iterator.next()); // { done: false, value: 2 } console.log(iterator.next()); // { done: false, value: 3 } console.log(iterator.next()); // { done: true, value: undefined } // 以后全部的调用都会返回相同的内容 console.log(iterator.next()); // { done: true, value: undefined }
生成器是一种返回迭代器的函数,经过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号能够紧挨着function关键字,也能够在中间加一个空格。redux
function *createIterator() { yield 1; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true } // 换个方法 function *createIterator(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); // iterator 与前面代码建立的 iterator 功能同样,能够试一下。
yield的使用限制
yield关键字只能在生成器内部使用,在其余地方使用会致使程序抛出语法错误,即使在生成器的函数里使用也如此。
function *createIterator(items) { items.forEach(function(item) { yield item + 1; }); } // 会报语法错误 node ./iterator.js
从字面理解,yield关键字肯定在createIterator()函数内部,可是它与return关键字同样,两者都不能穿透函数边界。嵌套函数中的return语句不能用做函数的返回语句,而此处嵌套函数中的yield语句会致使程序抛出语法错误。
生成器函数表达式&对象方法
经过上面的方法,关于函数表达式和对象方法,直接上代码吧,更明白。数组
// 函数表达式 let createIterator = function *(items) { for (let i =0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); // 对象方法 let o = { createIterator: function *(items) { yield items[i]; } } let iterator = o.createIterator([1, 2, 3]);
看过Symbol文章的小伙伴应该都知道,Symbol.iterator就是 well-known Symbol之一。可迭代对象就具备Symbol.iterator属性,它是一种与迭代器密切相关的对象。它经过指定的函数能够返回一个做用于附属对象的迭代器。在ES6中,全部的集合对象(Array, Set, Map)和字符串都是可迭代对象,这些对象中都有默认的迭代器。固然,ES中也添加了for-of循环这些可迭代对象。浏览器
这是解决循环内部索引跟踪问题的关键工具。
for-of循环每执行一次都会调用可迭代对象中的next()方法,并将迭代器返回的结果对象的value属性存储在一个变量中,循环将持续执行这一过程直到返回对象的done属性为true。异步
let values = [1, 2, 3]; for (let num of values) { console.log(num); } // 输出: // 1 // 2 // 3
示例说明:
for-of循环的代码经过调用values数组的Symbol.iterator方法来获取迭代器,这一过程是在Javascript引擎背后完成的。随后迭代器的next()的方法被屡次调用,从其返回对象的value属性读取值并存储在变量num中,直到对象的done为true时循环退出,因此num不会赋值为undefinedide
从上面的例子能够看出,可迭代对象都有一个默认迭代器。这个迭代器可经过Symbol.iterator来访问。函数
let values = [1, 2, 3]; let iterator = values[Symbol.iterator](); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true }
由此,咱们能够判断对象是否可迭代,是否是有更好的方法?工具
function isIterator(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterator([1, 2, 3])); // true console.log(isIterator("Hello")); // true console.log(isIterator(new Map())); // true console.log(isIterator(new Set())); // true console.log(isIterator(new WeakMap())); // false console.log(isIterator(new WeakSet())); // false
默认状况下,开发者定义的对象都是不可迭代的对象,但若是给Symbol.iterator属性添加一个生成器,则能够将其变为可迭代对象。ui
let collection = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for (let item of collection){ console.log(item); } // 输出: // 1 // 2 // 3
到这里,应该明白,ES6中已经默认为不少内建类型提供了内建迭代器,只有这些内容没法实现目标时,才须要本身建立。ES6中有三种集合对象: 数组、Map与Set集合,他们内建了如下三种迭代器:this
MDN 关于内建迭代器 说的比较简单,我就按“深刻理解ES6”来详细说一下吧。
特色:每次调用next()方法时,entries()返回一个数组,数组中两个元素,分别表示键和值。
- Array 第一个元素为数字类型的索引,第二个元素为值 - Set 第一个元素与第二个元素都是值,由于Set中值也做为键来使用 - Map 第一个元素为键,第二个元素为值 示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.entries()) { console.log(entry); } // 输出: // [ 0, 'red' ] // [ 1, 'green' ] // [ 2, 'blue' ] for (let entry of tracking.entries()) { console.log(entry); } // 输出: // [ 123, 123 ] // [ 456, 456 ] // [ 890, 890 ] for (let entry of data.entries()) { console.log(entry); } // 输出: // [ 'title', 'ES6之Iterator&Generator' ] // [ 'formate', 'net share' ]
特色: 返回集合中所存的全部值。
示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.values()) { console.log(entry); } // 输出: // red // green // blue for (let entry of tracking.values()) { console.log(entry); } // 输出: // 123 // 456 // 890 for (let entry of data.values()) { console.log(entry); } // 输出: // ES6之Iterator&Generator // net share
特色:返回集合中存在的每个键。
示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([ 123, 456, 890]); let data = new Map(); data.set("title", "ES6之Iterator&Generator"); data.set("formate", "net share"); for (let entry of colors.keys()) { console.log(entry); } // 输出: // 0 // 1 // 2 for (let entry of tracking.keys()) { console.log(entry); } // 输出: // 123 // 456 // 890 for (let entry of data.keys()) { console.log(entry); } // 输出: // title // formate
不一样集合,会使用不一样的默认迭代器,Array和Set使用的是values()迭代器,而Map则使用的是entries()迭代器。
特色: 与Array相似。string也能够经过下标访问字符内容。因为下标操做是编码单元而非字符,因此没法访问双字节符。在Unicode支持很差的版本(node或浏览器引擎),会出现错误。若是使用for-of,则不会出现这种问题,由于其操做的是字符而非编码单元。
思考一个问题:展开运算符与非数组可迭代对象
若是对可迭代对象使用展开运算,会调用默认迭代器吗?返回又是什么呢?
非数组可迭代对象呢?
示例:
let o = { a: 'aaaa', b: 123, c: 'ddd', *[Symbol.iterator]() { yield this.a; yield this.b; yield this.c; } } let ar = [...o]; console.log(ar); for(let item of ar) { console.log(item); } // 输出 // [ 'aaaa', 123, 'ddd' ] // aaaa // 123 // ddd
此次先作示例,再作说明。
示例1:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例2:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield first + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 6, done: false } // { value: 7, done: false } // { value: undefined, done: true }
示例3:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next(5)); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例4
function *createIterator() { let first = 1; yield first; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next(5)); console.log(iterator.next(4)); console.log(iterator.next(6)); console.log(iterator.next()); // 示例: // { value: 1, done: false } // { value: 3, done: false } // { value: 9, done: false } // { value: undefined, done: true }
示例5:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: NaN, done: false } // { value: NaN, done: false } // { value: undefined, done: true }
示例5的输出结果,是否是很意外?yield返回变量与正常变量赋值有何不一样?
分析:
这个也先示例,后说明。
示例1:
function *createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.throw(new Error('Boom'))); console.log(iterator.next(5)); // 输出: // { value: 1, done: false } // { value: 6, done: false } // xx/iterator.js:146 // let second = yield first + 2; // ^ // // Error: Boom
分析:前两个表达式正常求值,在继续执行let second 求值前,错误就会被抛出,并阻止了代码继续执行。
这个过程,与直接抛出异常很类似,只是抛出的时机不一样。
示例2
function *createIterator() { let first = yield 1; let second ; try { second = yield first + 2; } catch (ex){ second = 6; } yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next(4)); console.log(iterator.throw(new Error('Boom'))); console.log(iterator.next(5)); // 输出: // { value: 1, done: false } // { value: 6, done: false } // { value: 9, done: false } // { value: undefined, done: true }
分析:
用try...catch语句来捕获异常,包裹着第二名语句。尽管这条语句自己没有错误,但在给second赋值前,仍是会抛出错误,catch代码块捕捉到这个错误后,并把second = 6. 下一条yield语句继续执行后,返回9.
基本上两个示例能够归纳。
示例1
function *createIterator() { yield 1; return; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: undefined, done: true } // { value: undefined, done: true }
示例2
function *createIterator() { yield 1; return 33; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 33, done: true } // { value: undefined, done: true }
先看三个示例吧,这个只是语法规范。
示例1
function *createNumIterator() { yield 1; yield 2; } function *createColorIterator() { yield "red"; yield "green"; } function *createCombinedIterator() { yield *createNumIterator(); yield *createColorIterator(); yield true; } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 2, done: false } // { value: 'red', done: false } // { value: 'green', done: false } // { value: true, done: false } // { value: undefined, done: true }
分析:语法规范,多个迭代器合并,能够建立一个生成器,再给yield语句添加一个号,就能够将生成数据的过程委托给其余迭代器。当定义这些生成器时,只须要将号放置在关键字yield和生成器的函数名之间便可。
示例2
function *createNumIterator() { yield 1; yield 2; return 3; } function *createRepeatIterator(count) { for (let i = 0; i < count; i++){ yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumIterator(); yield *createRepeatIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出 : // { value: 1, done: false } // { value: 2, done: false } // { value: 'repeat', done: false } // { value: 'repeat', done: false } // { value: 'repeat', done: false } // { value: undefined, done: true }
分析:执行过程,先被委托给了createNumIterator(),返回值会被赋给变量result,执行到return 3时,返回精数值3。这个值随后被传入createRepeatIterator()方法。
可是,不管经过何种方式调用next()方法,数值3永远不会被返回,它只存在于createCombinedIterator()的内部。若是要输出3,参看示例3。
示例3
function *createNumIterator() { yield 1; yield 2; return 3; } function *createRepeatIterator(count) { for (let i = 0; i < count; i++){ yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumIterator(); yield result; yield *createRepeatIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); // 输出: // { value: 1, done: false } // { value: 2, done: false } // { value: 3, done: false } // { value: 'repeat', done: false } // { value: 'repeat', done: false } // { value: 'repeat', done: false } // { value: undefined, done: true }
期待的3,出来了。
生成器支持在函数中暂停代码执行,能够挖掘异步处理的更多方法。
示例:
function run(taskDef) { // 建立迭代器 let task = taskDef(); // 开始执行任务 let result = task.next(); function step() { if (!result.done) { result = task.next(); step(); } } step(); } run(function *() { console.log(1); yield; console.log(2); yield; console.log(3); yield; });
分析:
示例:
function run(taskDef) { // 建立迭代器 let task = taskDef(); // 开始执行任务 let result = task.next(); function step() { if (!result.done) { result = task.next(result.value); step(); } } step(); } run(function *() { let value = yield 1; console.log(value); value = yield value + 3; console.log(value); });
注意yield表达式求值。
示例:
// redux-saga 经典应用