阅读 169

JavaScript迭代器 | 8月更文挑战

理解迭代

在软件开发领域,迭代的意思就是按照顺序反复并且多次执行一段程序,在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
复制代码
文章分类
前端
文章标签