《深入理解ES6》第八章 迭代器与生成器

第八章 迭代器与生成器

《深入理解ES6》—— Nicholas C. Zakas

许多编程语言都将迭代器数据的方式从使用 for 循环转变到使用迭代器对象,
for循环需要初始化变量以便追踪集合内的位置,
而迭代器则以编程方式返回集合中的下一个项。

1. 循环的问题

var colors = [ "red", "green", "blue" ];

for ( var i = 0, len = colors.length; i < len; i++ ) {
    console.info( colors[ i ] );
}

此处使用 for 循环的标准方式,借助 i 变量来追踪 colors 数组中的位置索引。

虽然循环很直观,但当它被嵌套使用并要追踪多个位置索引时,情况就会变得非常复杂,额外的复杂度会引发错误。迭代器正是用来解决此问题的。

2. 迭代器

迭代器是被设计专用于迭代的对象,带有特定接口。

  • 迭代器对象都有 next() 方法
  • 迭代器持有一个指向集合位置的内部指针,每当调用 next() 会返回下一个值
  • next() 返回一个结果对象,结果对象有两个属性:valuedone
  • value 对应下一个值
  • done 是布尔值,为 true 时表示没有更多值可供使用

ES5 中创建迭代器:

function createIterator( items ) {
    var i = 0;
    return {
        next: function() {
            var done = ( i >= items.length );
            var value = !done ? items[ i++ ]: undefined;
            return {
                done,
                value
            }
        }
    }
}

var iterator = createIterator( [ 1, 2, 3 ] );

iterator.next(); //=> {done: false, value: 1}
iterator.next(); //=> {done: false, value: 2}
iterator.next(); //=> {done: false, value: 3}
iterator.next(); //=> {done: true, value: undefined}

根据ES6制定的规则来书写迭代器,是有一点复杂。

幸好,ES6提供了生成器

3. 生成器

3.1. 创建

生成器(generator)是能返回一个迭代器的函数,
*function 表示,可使用 yield 关键字。

function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}

let iterator = createIterator();

iterator.next(); //=> {value: 1, done: false}
iterator.next(); //=> {value: 2, done: false} 
iterator.next(); //=> {value: 3, done: false}
iterator.next(); //=> {value: undefined, done: true}

3.2. yield

yield 关键字是ES6新增的,
指定了迭代器在被 next() 方法调用时应当按顺序返回的值。

生成器函数会在每个 yield 语句后停止执行。
例如,此代码中 yield 1 执行后,该函数将不再执行任何操作,
直到迭代器的 next() 方法被调用,此时才继续执行 yield 2

function *createIterator( items ) {
    for ( let i = 0; i < items.length; i++ ) {
        yield items[ i ];
    }
}

let iterator = createIterator( [ 1, 2, 3 ] );

iterator.next(); //=> {value: 1, done: false}
iterator.next(); //=> {value: 2, done: false}
iterator.next(); //=> {value: 3, done: false}
iterator.next(); //=> {value: undefined, done: true}

生成器函数也是函数,能被用于可用函数的位置。

yield 无法用在嵌套函数内。

3.3. 生成器函数表达式

let iterator = function *( items ) {
    for ( let i = 0; i < items.length; i++ ) {
        yield items[ i ];
    }
}

3.4. 生成器对象方法

由于生成器就是函数,因此也可以被添加到对象中。

var o = {
    createIterator: function *( items ) {
        for ( let i = 0; i < items.length; i++ ) {
            yield items[ i ];
        }
    }
};

也可以使用ES6方法简写方式:

var o = {
    *createIterator( items ) {
        for ( let i = 0; i < items.length; i++ ) {
            yield items[ i ];
        }
    }
}

4. 可迭代对象与 for-of

可迭代对象(iterable)是包含 Symbol.iterator 属性的对象。
可通过 Symbol.iterator 这个属性可以获取指定对象的默认迭代器函数。

ES6中,所有的集合对象(数组、Set、Map、字符串)都是可迭代对象。

let values = [ 1, 2, 3 ];
for (let num of values) {
    console.info( num );
}

for-of 循环在循环每次执行时会调用可迭代对象的 next() 方法,
并将结果对象的value属性的值存储在一个变量上。
循环过程会持续到结果对象的done属性变成true为止。

4.1. 访问默认迭代器

可使用 Symbol.iterator 属性来访问对象上的默认迭代器:

let values = [ 1, 2, 3 ];
let iterator = values[ Symbol.iterator ]();

iterator.next(); //=> {value: 1, done: false}
iterator.next(); //=> {value: 2, done: false}
iterator.next(); //=> {value: 3, done: false}
iterator.next(); //=> {value: undefined, done: true}

也可使用 Symbol.iterator 来检测一个对象是否能进行迭代:

let str = "Hello";
typeof str[ Symbol.iterator ] === "function"; //=> true

let set = new WeakSet();
typeof set[ Symbol.iterator ] === "function"; //=> false

4.2. 创建可迭代对象

给对象设置Symbol.iterator属性,该属性的值为迭代器函数,则此对象就可迭代了。

let collection = {
    items: [],
    [Symbol.iterator]: function *() {
        for ( let item of this.items ) {
            yield item;
        }
    }
};

collection.items.push( 1 );
collection.items.push( 2 );
collection.items.push( 3 );

for ( let val of collection ) {
    console.info( val );
}

5. 内置的迭代器

迭代器是ES6的一个重要部分,你无需为许多内置类型创建你自己的迭代器,
语言已经默认包含它们了。

只有当内置的迭代器无法满足你的需要时,才有必要创建自定义的迭代器。

5.1. 集合的迭代器

ES6具有三种集合对象类型:数组、Map、Set。都有如下迭代器:

  • entries():返回一个包含键值对的迭代器。
  • values():…集合中的值…。
  • keys():…集合中键…。

for-of 循环没有显式指定迭代器时,
每种集合类型都有一个默认的迭代器供循环使用。

  • Set:values()
  • Map:entries()

5.2. NodeList 的迭代器

文档对象模型(DOM)具有一种 NodeList 类型,
用于表示页面文档中的元素的集合。

DOM关于NodeList的规定(HTML规范)也包含了一个默认迭代器。这意味着可以使用 for-of

var divs = document.getElementByTagName( "div" );
for ( let div of divs ) {
    console.info( div.id );
}

5.3. 扩展运算符

由于扩展运算符能用在任意可迭代对象上,
它就成为了将可迭代对象转换为数组的最简单方法。

let map = new Map( [ 
    [ "name", "吴钦飞" ],
    [ "gender", "男" ]
] );
[ ...map ]; 
//=> [["name","吴钦飞"],["gender","男"]]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值