我一直在实现ES6Set对象的一个有用的子类。对于我的许多新方法,我想接受一个参数,它可以是另一个集合或数组,也可以是我可以迭代的任何东西。我在我的接口中称它为"不可修改的",只在它上面使用.forEach()(对于一个集合或一个数组来说,它工作得很好)。示例代码:
1 2 3 4 5 6 7 8 9 10 11
| // remove items in this set that are in the otherIterable
// returns a count of number of items removed
remove(otherIterable) {
let cnt = 0;
otherIterable.forEach(item => {
if (this.delete(item)) {
++cnt;
}
});
return cnt;
} |
或
1 2 3 4 5 6
| // add all items from some other iterable to this set
addTo(iterable) {
iterable.forEach(item => {
this.add(item);
});
} |
但是,我怀疑我可能不支持ES6定义它的方式中的任何ITerable,所以我对JavaScript ITerable的实际定义使用的术语和ES6规范一样感兴趣?
如何在ES6 JavaScript中测试它?
您应该如何迭代一个通用的iterable?
我在ES6规范中发现了这样的短语:
If the parameter iterable is present, it is expected to be an object
that implements an @@iterator method that returns an iterator object
that produces a two element array-like object whose first element is a
value that will be used as a WeakMap key and whose second element is
the value to associate with that key.
但是,这指的是我似乎无法通过该属性名访问的@@iterator method。
- 这不是"@@iterator"应该是"系统"符号实例或什么?编辑我想是Symbol.iterator。
- @有意义——也许吧,但我不确定这些符号是如何工作的,我应该如何使用它,或者这对于我应该如何迭代对象意味着什么。
- 符号实例用作对象属性名称。它们从不可数。就像你可以做foo[Symbol.iterator] = function() ...一样(或者把它放在原型上)
- 如果我理解你的问题,我想这应该是个答案。
- 我不确定是否应该将其作为ES 6的副本关闭:symbol.iterator和@@iterator之间的差异,或者在ES6 javascript中@@("at")是什么意思?或者我们是否应该编辑问题并回答,iterable是一个使用迭代器方法的对象。
- @我不认为另一个问题解释了你应该如何迭代你发现的不可迭代的东西。它确实有助于解释Symbol.iterator的事情。
- @jfriend00是的,我的问题是它同时询问"如何测试/使用iterables"(同时在引号中提供答案)和"什么是@@iterator的意思以及如何将其用作属性"。你能把它编辑成其中一个吗?(或者把它分成第二个岗位)
- @伯吉-我正试图解决一个涉及两者的问题(所以为什么我展示了实际的问题,而不仅仅是问一个理论问题)。这个问题真的不能涵盖问题的两个方面吗?
- 我只是认为我们只会把实际问题放在问题中,而不讨论如果他们想使用你找到的报价,那么对于答案,@@iterator意味着什么,或者不讨论它,而是放一个链接。
What is the real definition of a Javascript iterable using the term as the ES6 specification does?
§25.1.1.1定义了"ITerable接口"。
它们是具有返回有效迭代器的Symbol.iterator键方法的对象(而该迭代器又是根据第25.1.1.2条预期其行为正常的对象)。
How do you test for it in ES6 Javascript?
我们不能在不调用@@iterator方法的情况下测试它返回的内容,也不能在不尝试运行它的情况下测试结果是否符合Iterator接口。最好的办法是
1 2 3
| function looksIterable(o) {
return typeof o[Symbol.iterator] =="function";
} |
然而,我通常不会测试这个,只是让它失败,当它不是不可测的时候例外。
How should you iterate a generic iterable?
不要使用forEach。(事实上,不要在ES6中的任何地方使用forEach)。
迭代的正确方法是一个for (… of …)循环。它执行所有的ITerability检查(使用抽象的GetIterator操作和运行(甚至关闭)迭代器,并在不可ITerable值上使用时抛出适当的TypeErrors。
- 我去把一些.forEach()代码转换成for/of。大多数转换很简单,但是使用.forEach()提供的index参数的函数需要额外的代码来跟踪for/of提供的索引。我不确定我是否准备好说一个人永远不应该使用.forEach()。当然,当您不使用.forEach()的index参数时,似乎没有理由将其与es6一起使用。我使用.forEach()的一个原始原因是它可以与es5中的polyfill集合一起使用,而for/of不能被polyfill(必须被发现)。
- 在你需要指数的大部分时间里,arr.entries()是你的朋友。如果需要任意iterables的索引,可以将额外的代码抽象到function* withIndex(it) { let i=0; for (let v of it) yield [i++, v]; }中,并像使用for (let [i, el] of withIndex(set)) …那样使用它。
- 是的,当然可以这样做(或者其他很多方式)。不过,当您需要每次迭代的值和索引时,它们并不比.forEach()简单。为什么你这么强烈地建议反对.forEach(),因为这是你所做的最简单的匹配?您只是想避免它使用的多个函数调用吗?
- 是的,回调调用的开销是其中一个原因(诚然,我不知道它与生成器的开销相比有多大,也不知道哪一个更容易得到优化者的关注)。另外两个:iterables比"foreachable"更通用(更好),我绝对不喜欢使用forEach进行副作用编程(特别是在map或reduce更合适的情况下选择),因此我更喜欢使用循环作为命令式风格与纯功能式的有用区别。也许我应该写一篇被认为有害的文章。
- 从字里行间看,我觉得你讨厌人们使用.forEach()是因为错误的原因(例如,不是.map()和.reduce()),现在我们有了for/of(如果你在ES6环境中编程或发生),使用.forEach()的理由甚至更少,但实际上听上去不像你有一个合法的理由完全摆脱它,当它是合法最简单的匹配你所做的。
- 是的,我想这很好地概括了一下,我只是养成了这样的习惯:"根本不要使用forEach",它适用于99%的es6用例,而不是详细说明它的合法性(可能只有readLines().map(…).filter(…).forEach(writeLine)这样的东西,或者我的击键特别宝贵)。
符号构造器Symbol.iterator上有一个特殊属性,它的值是,呃,我猜你会说"概念"属性名@@iterator。因此,可以为这样的对象创建迭代器方法:
1 2 3
| object[Symbol.iterator] = function* () {
// do something that makes sense
}; |
您还可以通过执行以下操作来测试某个对象是否是迭代器
1
| if (Symbol.iterator in object) |
(也可以检查它是否是一个函数)。现在,这些迭代器函数(Symbol.iterator属性的值)是生成器函数(不是我在示例中编辑的*)。因此,您首先调用函数并保存返回的对象,然后调用.next()来启动它们并获取值。这将得到一个具有value和done属性的对象。
您也可以让for ... of或"spread"...运算符担心迭代器函数。
- 所以,这就回答了你如何测试某个东西是否是官方的不可测物。但是,那么您应该如何以最一般的方式迭代它呢?你只是应该使用for/of吗?
- @当然可以,或者你可以把它转换成一个带有Array.from()或破坏性赋值的数组。
- 还要注意,迭代器函数是我认为应该是一个function*函数,但我不能100%确定我真的理解它。(固定答案)
- function*是生成返回迭代器对象的函数的一种方法,但是有一个正常函数返回一致的迭代器对象是可以的。
- @洛根神话,好的,当然,这是完全有道理的。