理解迭代
在软件开发领域,迭代的意思就是按照顺序反复并且多次执行一段程序,在 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
复制代码
评论