理解迭代
在软件开发领域,迭代的意思就是按照顺序反复并且多次执行一段程序,在 JavaScript 中,计数循环就是一种最简单的迭代,直接上代码
for (let i = 1;i < 5;i++){ console.log(i) //1 2 3 4
复制代码
循环是迭代机制的基础,因为它可以指定迭代的次数,每次迭代执行什么操作,及多会儿停止迭代
ES5 新增了 Array.prototype.forEach()方法,可以进行单独记录索引及通过数组对象取得值(不够理想)因为这个方法只适用于数组,且回调比较笨拙,也无法标记何使终止。
let collection = ['name','age','six'];collection.forEach( (item) => console.log(item));//name age six
复制代码
迭代器
迭代器是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象,迭代器会暴露其关联可迭代对象的 API
任何实现iterable接口的数据结构都可以被实现iterator接口的结构进行迭代。
可迭代协议
实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和创建实现 Iterator 接口的对象的能力。在 ECMAScript 中,这意味着必须暴露一个属性作为“默认迭代器”,而且这个属性必须使用特殊的 Symbol.iterator 作为键。这个默认迭代器属性必须引用一个迭代器工厂函数,调用这个工厂函数必须返回一个新迭代器。
字符串、数组、映射、集合、arguments 对象、NodeList 等 DOM 集合类型都实现了 iterable 接口
let str = 'abc';let arr = ['a', 'b', 'c'];let map = new Map().set('a', 1).set('b', 2).set('c', 3); let set = new Set().add('a').add('b').add('c'); let els = document.querySelectorAll('div');// 这些类型都实现了迭代器工厂函数console.log(str[Symbol.iterator]); // f values() { [native code] } console.log(arr[Symbol.iterator]); // f values() { [native code] } console.log(map[Symbol.iterator]); // f values() { [native code] } console.log(set[Symbol.iterator]); // f values() { [native code] } console.log(els[Symbol.iterator]); // f values() { [native code] }// 调用这个工厂函数会生成一个迭代器console.log(str[Symbol.iterator]()); // StringIterator {} console.log(arr[Symbol.iterator]()); // ArrayIterator {} console.log(map[Symbol.iterator]()); // MapIterator {} console.log(set[Symbol.iterator]()); // SetIterator {} console.log(els[Symbol.iterator]()); // ArrayIterator {}
复制代码
迭代器协议迭代器 API 使用 next()方法 在可迭代对象中遍历数据。每次成功调用 next(),都会返回一个 IteratorResult 对象,其中包含迭代器返回的下一个值。若不调用 next(),则无法知道迭代器的当前位置。
next()方法返回的迭代器对象 IteratorResult 包含两个属性:done 和 value。done 是一个布尔值,表示是否还可以再次调用 next()取得下一个值;value 包含可迭代对象的下一个值(done 为 false),如果 value 的值为 undefined(done 为 true)。done 为 true 的话表示没有下一个了。
let collection = ['name','age','six']; let item = collection[Symbol.iterator](); console.log(item);//Array Iterator {} console.log(item.next());//{value: "name", done: false} console.log(item.next());//{value: "age", done: false} console.log(item.next());//{value: "six", done: false} console.log(item.next());//{value: undefined, done: true}
复制代码
迭代器是使用游标来记录遍历可迭代对象的,如果在迭代期间被修改,迭代器也会马上反映相应的变化
let arr = ['foo', 'baz']; let iter = arr[Symbol.iterator]();console.log(iter.next());// { done: false, value: 'foo' } // 在数组中间插入值arr.splice(1, 0, 'bar'); console.log(iter.next()); // { done: false, value: 'bar' } console.log(iter.next()); // { done: false, value: 'baz' } console.log(iter.next()); // { done: true, value: undefined }
复制代码
注意:迭代器维护着一个指向可迭代对象的引用,因此迭代器会阻止垃圾回收程序回收可迭代对象。
提前终止迭代器
一般我们用 return()方法来关闭执行迭代的逻辑。return 方法必须返回一个返回值,我们可以只返回 done:true
let a = [1, 2, 3, 4, 5]; let iter = a[Symbol.iterator](); iter.return = function() { console.log('Exiting early'); return { done: true }; }; for (let i of iter) { console.log(i); if (i > 2) { break } } //1 2 3 Exiting early
复制代码
for-of 循环也可以过过 berak、continue、return、throw 提前退出
let counter = [1,2,3,4,5]; for( let i of counter){ if(i>3){ break; } console.log(i); }
复制代码
如果迭代器没有关闭,则还可以继续从上次离开的地方继续迭代
let counter = [1,2,3,4,5]; for( let i of counter){ console.log(i); if(i>3){ break } //1 2 3 } for( let i of counter){ console.log(i); }//4 5
复制代码
为了让一个可迭代对象能够创建多个迭代器,必须每创建一个迭代器就对于一个新计数器,为此我们可以把计数器变量放到闭包里,然后通过闭包返回迭代器
class Counter { constructor(limit) { this.limit = limit; } [Symbol.iterator]() { let count = 1, limit = this.limit; return { next() { if (count <= limit) { return { done: false, value: count++ }; } else { return { done: true, value: undefined }; } } }; } } let counter = new Counter(3); for (let i of counter) { console.log(i); } // 1 // 2 // 3 for (let i of counter) { console.log(i); } // 1 // 2 // 3
复制代码
评论