如何实现Symbol.iterator?

How to implement Symbol.iterator?

我正在尝试创建 Set 的子类,由于我不能简单地从它扩展,所以我包装了它的功能。

我正在尝试实施 Symbol.iterator 方法,但 Flow 有 none 个方法。

这是我的代码:

/* @flow */
class CSet<T> {
    _set: Set<T>;
    [Symbol.iterator](): Iterator<T> {
        return this._set[Symbol.iterator];
    }
}

var a: CSet = new CSet();
for(var b of a){

}

core.js:309:5,29: property @@iterator
Property not found in
test.js:2:7,10: CSet

test.js:4:2,6:2: 
computed property keys not supported

第二个错误没什么大不了的,因为我可以很容易地抑制它。我想知道我是否只是一起做错了什么。

试试这个 javascript 代码

// module -- start

let map = new WeakMap();

class Foo {
  constructor(any) {
    map.set(this, new Set(any));
  }
  values() {
    return map.get(this).values();
  }
  [Symbol.iterator]() {
    return this.values();
  }
}

// module -- end


for(let item of new Foo([1,2,3])) {
  console.log(item);
}

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
  2. sandbox

因为 Flow 目前没有对符号的一般支持,它表示 Symbol.iterator 的方式很老套,并且现在基本上阻止了在用户空间中定义迭代器的能力(它的支持只在库定义中有效): (

特别是 Flow 期望一个可迭代对象上有一个 @@iterator 属性(这肯定不是一个有效的 属性 名称——但这是一个临时的 hack 以获得支持库定义)。

因此,在获得适当的符号支持之前,您最好的解决方法是为该模块创建一个库定义,该模块使用此 @@iterator 属性 来促进 Flow 的理解:

// RealModule.js
export class CSet {
    [Symbol.iterator]() {
        return this._set[Symbol.iterator];
    }
}

.

// RealModule.FlowLibDef.js
declare module RealModule {
    declare class CSet<T> {
        _set: Set<T>;
        @@iterator(): Iterator<T>;
    }
}

我发现了一个技巧,可以让您将用户定义的 class 标记为可迭代,而无需为整个 class.

编写和维护并行 libdef

关键是写一个专用的 superclass 来实现 [Symbol.iterator](),并为那个 superclass 提供一个 libdef:

// IterableBase.js
export default class IterableBase {
  [Symbol.iterator]() {
    return this._Symbol_iterator();
  }
}
// IterableBase.js.flow
// @flow
declare class IterableBase<T> {
  @@iterator(): Iterator<T>;
}
export default IterableBase;

现在您可以让您的自定义 class 扩展 IterableBase 并实现替代方法名称:

// RealModule.js
// @flow
import IterableBase from './IterableBase';

class CSet<T> extends IterableBase<T> {
  _Symbol_iterator(): Iterator<T> {
    ...
  }
}

这显然仍然是一种 hack,但它应该比替代方法更简单、更安全。

// @flow
class MyCollection<T> {
    /*:: @@iterator(): Iterator<T> { return ({}: any); } */

    // $FlowFixMe: computed property
    [Symbol.iterator](): Iterator<T> {
        // NOTE: this could just as easily return a different implementation
        return new MyIterator(this);
    }
}

class MyIterator<+T> {
    /*:: @@iterator(): Iterator<T> { return (this: any); } */

    // $FlowFixMe: computed property
    [Symbol.iterator](): Iterator<T> {
        return this;
    }

    next(): IteratorResult<T, void> {
        return { done: false, value: someT };
        // or return { done: true, value: undefined };
    }
}

for (const value of new MyCollection()) {
    console.log(value);
}

之所以可行,是因为流程将 /*:: code */ 解释为源代码中的流程代码,只是在运行时将其注释掉,因此实际上不会影响代码。

Flow 本质上知道 @@iterator 方法,尽管它无效 JavaScript,因此我们将其定义为现有的,returning 一个 Iterator<T>,并且 returning 一个适用于它的值(即一个空对象转换为 any)。

计算的 属性 方法随后被流程完全忽略,就好像它根本没有定义一样。至关重要的是,您实际上 return 来自该方法的有效 Iterator<T>,否则事情会在运行时中断。