JavaScript中的Symbol.iterator

这是一篇简短但有用的文章,对于将来使用JavaScript迭代器的开发人员来说。


图片


在我们了解js中的迭代器之前,请记住什么是Symbol


Symbol是唯一且不变的标识符。 使用Symbol() 函数创建 ,也可以标记为Symbol('foo')。 具有相同标签的符号彼此不相等,通常,任何符号都不彼此相同(请记住唯一性)。

有系统符号,例如Symbol.iteratorSymbol.toPrimitive等。 系统字符由语言本身使用,但是我们也可以使用它们来更改某些对象的默认行为。


符号是es6规范的一部分,因此根本就不支持它们( caniuse )。


关于Symbol.iterator


基本上,在迭代对象的属性时,语言会在for ... of循环中使用此符号。 它也可以直接用于内置数据类型:


const rangeIterator = '0123456789'[Symbol.iterator](); console.log(rangeIterator.next()); // {value: "0", done: false} console.log(rangeIterator.next()); // {value: "1", done: false} console.log(rangeIterator.next()); // {value: "2", done: false} ... console.log(rangeIterator.next()); // {value: "9", done: false} console.log(rangeIterator.next()); // {done: true} 

此示例适用于字符串,因为String.prototype具有自己的迭代器( spec )。 js中可迭代类型的列表: 字符串,数组,TypedArray,映射,集合。
除了循环外,javascript在以下构造中使用Symbol.iterator传播算子,yielddestructuring分配


调用[Symbol.iterator]()返回一个迭代器接口,如下所示:


 Iterator { next(); //    return(); //   throw(); //   } 

.next()、. return()、. throw()方法准备(然后我们将了解如何)方法并返回以下形式的对象:


 { value - ,   done -    } 

例如,当迭代过早结束时,将使用.return()和.throw()方法。 您可以在ecmascript规范中阅读有关它们的更多信息。


在其结构中使用Symbol.iterator


作为示例,让我们创建自己的结构,可以使用...的...对其进行迭代,还可以将Symbol.iterator与上述语言结构一起使用。


想象一下,我们有一条穿越多个站点的路线,并且我们想沿着该路线对每个站点进行操作,例如,将其显示在控制台中。


创建一个路由类:


 class Route { stations; //      constructor(stations) { this.stations = stations; } //     id get(idx) { return this.stations[idx]; } //   [Symbol.iterator]() { return new RouteIterator(this); //   } } 

如您所见,我们的Route实现了Symbol.iterator方法,因此Route是一个可迭代的实体( spec ),这意味着我们可以使用for ... of (在研究RouteIterator的实现之后 )进行遍历


[Symbol.iterator]()方法将被调用与调用它一样多的次数。 也就是说,如果几个周期试图一个接一个地遍历该路线 ,则将为每个周期调用[Symbol.iterator]() ,因此对于每个调用,我们都会创建一个RouteIterator的新实例。


现在让我们了解RouteIterator本身。 此类为Route实体实现迭代器接口。 让我们看一下:


 class RouteIterator { _route; //     _nextIdx; //    constructor(route) { this._route = route; this._nextIdx = 0; } next() { if (this._nextIdx === this._route.stations.length) { return { done: true } //     } const result = { value: this._route.get(this._nextIdx), done: false } this._nextIdx++; return result; } } 

在此类中,我们可以访问可迭代的集合( route属性),并且nextIdx是指向集合中下一个值的指针。


next()方法首先检查路由是否已完成,如果已完成,则返回迭代已完成。 否则,我们将取路由集合中的下一个值,即迭代未完成,移动指针并返回结果。


现在,我们可以通过...的路线收集:


 const route = new Route(['', '', '']) for (let item of route) { console.log(item); } 

此代码将列出我们传递给Route的站点。


现在,我们将使用函数生成器浏览各个工作站:


 function* gen() { yield* route; return 'x'; //        .next() } const g = gen(); g.next() // {value: "", done: false} g.next() // {value: "", done: false} g.next() // {value: "", done: false} g.next() // {value: 'x', done: true} g.next() // {value: undefined, done: true} 

Symbol.iterator用于重组


 const [a, b, c] = route; // a - "" // b - "" //  - "" 

传播算子:


 function test(a, b, c) { console.log(a, b, c) } test(…route) // "" "" "" 

结果


我们创建了我们的类,使其变得可迭代并与javascript构造一起使用。 谢谢您的关注=)。


用料


仅一篇文章就不可能完全掌握新材料,因此这里有一些其他内容:
关于Guru重构书中的迭代器模式
关于Ilya KantorMDN上的Symbol

Source: https://habr.com/ru/post/zh-CN481548/


All Articles