如何实现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);
}
见
因为 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>
,否则事情会在运行时中断。
我正在尝试创建 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);
}
见
因为 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>
,否则事情会在运行时中断。