JS - iterator(迭代器)

一、 概念

       JavaScript原有表示“集合”的数据结构,主要是数组(' Array ')和对象(' Object ' ),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理不同的数据结构。
       遍历器(Iterator)就是这样一种机制。它是一种接口,为不同的数据结构提供一种访问机制,即for ... of 循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

二、本质 

        迭代器对象本质上,就是一个指针对象。通过指针对象的next(), 用来移动指针。
       
迭代器协议:对象必须提供一个next(),执行该方法要么返回迭代的下一项,要么就引起Stopiteration异常,以终止迭代。
        每调用一次next ()方法,都会返回一个对象,都会返回数据结构的当前成员的信息。这个对象有 value 和 done 两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否有必要再调用一次next () 。对于遍历器来说,value:undefined和done:false属性都是可以省略的。
        
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性上;或者说,一个数据结构只要有Symbol.iterator属性,就认为是可遍历的。

三、实现Iterator接口的原生对象

原生具备Iterator接口的数据结构有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

 可以看到Array原型对象已经实现了Iterator这个属性:

 那么数组的实例对象也拥有这个属性,可以调用试试:

下面是模拟next()方法返回的例子:

       上面的代码定义了一个makeIterator函数,它是一个遍历器生成的函数,作用就是返回一个遍历器对象。对数组[ ’a', 'b' ]执行这个函数,就会返回该数组的遍历器对象(指针对象)it。指针对象的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向a;第二次调用,指向b
        总之,调用指针对象的next()方法,就可以遍历事先给定的数据结构。
        上面说了,对于遍历器对象来说, done:false 和 value:undefined属性都是可以省略的,因此上面的makeIterator函数可以简写成下面的形式。

 使用场合:

① 对实现了Iterator接口的数据解构赋值

 ② 扩展运算符

上面代码的扩展运算符内部就调用 Iterator 接口。

实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。

三、 for... of 循环

for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、字符串等。

数组原生具备iterator接口,(默认部署Symbol.iterator属性),for...of 循环本质上就是调用这个接口产生的遍历器:

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

for(let v of obj) {
  console.log(v); // red green blue
}

上面代码中,空对象obj部署了数组arrSymbol.iterator属性,结果objfor...of循环,产生了与arr完全一样的结果。

for ... of 循环可以代替数组实例 forEach 方法。

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

四、 Set 和 Map结构

Set和Map结构也原生具有Interator接口,可以直接使用for...of循环。

var engines = new Set(['1','2','3']);
for (var e of engines) {
  console.log(e);   // 1  2  3
} 

var class = new Map();
class .set('Tom', 12);
class .set('Lala', 13);
class .set('9300', 14);
for (var [name,value] of class) {
  console.log(name + ':' + number);    // Tom:12  Lala:13  9300:14
}     

以上代码演示了如何遍历Set和Map结构。需要注意的是,首先遍历的顺序是按照哥哥成员被添加进数据结构的顺序。其次,Set结构遍历时,返回的是一个值。而Map结构遍历时,返回的是一个数组,该数组的两个成员分别是当前Map成员的键名和键值。

let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

for (let [key, value] of map) {
  console.log(key + ' : ' + value);
}
// a : 1
// b : 2

其他内容可参照 ES6入门

猜你喜欢

转载自blog.csdn.net/m0_52545254/article/details/126741625