ES6之迭代器与生成器

206 阅读4分钟

迭代器是一个特殊的对象,具有专门为迭代过程设计的专有接口,所有迭代器对象都有一个 next() 方法,每次调用返回一个结果对象。迭代器保存一个内部指针,用于指向当前集合中值的位置,调用 next() ,返回下一个可用的值。

结果对象包含两个属性:value 和 done,value 表示下一个返回值,done 是一个布尔值,当没有更多可返回数据时返回 true。 结果对象两种形式:

value 下一个返回值(不为undefined),done 为 false value 为 undefined ,done 为 true

ES6 中迭代器编写规则复杂,但 ES6 同时引入生成器对象,它使创建迭代器对象的过程变得更简单。下面先介绍生成器

生成器

生成器是一种返回迭代器的函数,生成器调用方式与普通函数相同,只不过返回的是一个迭代器。

function *createIterator(){
    yield 1;
    yield 2;
}
let iterator = createIterator();
console.log(iterator.next());   //  Object { value: 1, done: false }
console.log(iterator.next());   //  Object { value: 2, done: false }
console.log(iterator.next());   //  Object { value: undefined, done: true }

上述代码中:

createIterator() 前的星号表明它是一个生成器,我们知道函数还有一种 new 方式调用,通过 new 调用生成器的话会抛出错误,生成器是没有 [Constructor] 属性。 yield 关键字是 ES6 新特性,下面来介绍它。

yield 使用

yield 关键字,通过它来指定调用迭代器 next() 返回值及返回顺序。每执行完一条 yield 语句后函数就会自动停止执行,直到再次调用迭代器的 next() 或 throw() 方法才会继续执行。

function *createIterator (array) {
    for(let i = 0; i < array.length; i++){
         yield array[i];
    }
}
let iterator = createIterator([1,2]);
console.log(iterator.next().value);     //  1
console.log(iterator.next().value);     //  2

yield 使用限制:只能在生成器内部使用,在其他地方使用或生成器内部的函数使用都会抛出语法错误。

yield 1;    //  抛出语法错误    

function *fun (array) {
    //  可以使用
    array.forEach(function () {
        //  不可以使用
        yield 1;    //抛出语法错误
    })
}

生成器表达式

注意不能用箭头函数来创建生成器。

//  函数表达式
let createIterator = function *(array) {
    for(let i = 0; i < array.length; i++){
        yield array[i];
    }
}

//  对象的生成器
let object = {
    array : [1,2],
    //  第一种方式:
    createIterator: function *() {
        for(let i = 0, len = this.array.length; i < len; i++){
            yield this.array[i];
        }
    }
    
    //  第二种方式:简写方式
    *createIterator () {
        for(let i = 0, len = this.array.length; i < len; i++){
            yield this.array[i];
        }
    }
}

迭代器

可迭代对象

可迭代对象具有 Symbol.iterator 属性,是一种与迭代器密切相关的对象。 Symbol.iterator 通过指定函数可以返回一个作用于附属对象的迭代器。在 ES6 的所有集合对象和字符串都是可迭代对象,它们都有默认的迭代器,就是通过 Symbol.iterator() 来获取的。

for-of 循环中,首先使用对象默认迭代器[Symbol.iterator],然后每执行一次都会调用迭代器的next(),从其返回对象的value属性读取值并存储在变量中。

let array = [1,2,3];
//  for-of
for (let num of array) {
    console.log( num );         
}
//依次:1, 2, 3

//  模拟
let iterator = array[Symbol.iterator]();
let num = iterator.next().value;
while ( num !== undefined ) {
    console.log(num);
    num = iterator.next().value;
}
//依次:1, 2, 3

通过上述代码,明白在 JS 引擎中执行 for-of 循环语句中,通过 Symbol.iterator 获取数组 array 的默认迭代器,并用它遍历数组元素。 下面利用 Symbol.iterator 创建一个可迭代对象。

//  创建一个可迭代对象
let object = {
    array: [1,2,3],
    *[Symbol.iterator] () {
        for( let number of this.array){
            yield number;
        }
    }
}

for ( let number of object ) {
    console.log( number );
}

默认情况下,对象是不可迭代的,但如果给 Symbol.iterator 属性添加一个生成器,则该对象将变为可迭代对象。

内建迭代器

ES6 中有 3 中类型的集合对象:数组、Set集合和Map集合。这3中对象都内建了以下三种迭代器:

  • entries() 返回一个迭代器,其值为多个键值对
  • values() 返回一个迭代器,其值为集合的值
  • keys() 返回一个迭代器,其值为集合中所有键名
let array = [1,2,3];
//  entries()
for( var item of array.entries() ) {
    console.log(item);
}
//  依次打印:Array [ 0, 1 ], Array [ 1, 2 ], Array [ 2, 3 ]

//  values()
for( var item of array.values() ) {
    console.log(item);
}
//  依次打印:1, 2, 3

//  keys()
for( var item of array.keys() ) {
    console.log(item);
}
//  依次打印:0, 1, 2

每个集合类型都有一个默认的迭代器,在 for-of 循环中,数组和Set集合调用 values() 迭代器,Map集合调用 entries() 迭代器。

//  数组
let array = [1,2,3];
for ( let item of array ) {
    console.log(item);      //  依次打印:1,2,3
}

//  Set集合
let set = new Set([1,2,3]);
for ( let item of set ) {
    console.log(item);      //  依次打印:1,2,3
}

//  Map集合
let map = new Map([["key1", "value1"], ["key2", "value2"]]);
for ( let item of map ) {
    console.log(item);  
    //依次打印:Array [ "key1", "value1" ],Array [ "key2", "value2" ]
}

展开运算符与迭代器

ES6 新增加的展开运算符(...),它可以操作所有可迭代对象,根据默认迭代器来选取要引用的值,从迭代器读取所有值,然后按顺序返回。

let object = {
    array: [1,2],
    *[Symbol.iterator] () {
        for ( let item of this.array ) {
            yield item;
        }
    }
}
let array = [...object];
console.log(array);     //  Array [ 1, 2 ]