如何实现Symbol.iterator?

时间:2015-08-11 13:04:19

标签: javascript flowtype

我正在尝试创建Set的子类,因为我不能简单地从它扩展,我正在包装它的功能。

我正在尝试实现Symbol.iterator方法,但Flow没有。

这是我的代码:

/* @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

第二个错误不是很大的交易,因为我可以轻易地压制它。我想知道我是不是一起做错了什么。

4 个答案:

答案 0 :(得分:5)

因为Flow目前没有对符号的一般支持,所以它代表Symbol.iterator的方式是hacky并且基本上阻止了在用户空间中定义迭代器的能力(它的支持仅适用于库定义):( < / p>

特别是Flow期望一个iterable上有一个@@iterator属性(肯定不是一个有效的属性名称 - 但这是获得库定义支持的暂时黑客攻击)。

因此,在获得正确的符号支持之前,最好的解决方法是为此模块创建一个库定义,使用此@@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>;
    }
}

答案 1 :(得分:3)

// @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);
}

这个工作的原因是flow解释/*:: code */,好像它是源代码中的流代码,除了它在运行时被注释掉,因此实际上不会影响代码。

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

然后,flow会完全忽略计算属性方法,就好像它根本没有定义一样。实际上从方法中返回有效的Iterator<T>是至关重要的,否则事情会在运行时中断。

答案 2 :(得分:1)

我发现了一个技巧,可以让用户定义的类标记为可迭代,而无需为整个类编写和维护并行的libdef。

关键是编写一个实现[Symbol.iterator]()的专用超类,并为该超类提供一个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;

现在,您可以让自定义类扩展IterableBase并实现替换方法名称:

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

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

这显然仍然是一个黑客,但它应该比替代方案更容易和更安全。

答案 3 :(得分:0)

试试这个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