为什么 Javascript `iterator.next()` return 是一个对象?
Why does Javascript `iterator.next()` return an object?
求助!在用 C# 编程了一段时间后,我开始喜欢上 Javascript,但我一直在学习喜欢可迭代协议!
为什么 Javascript 采用需要为每次迭代创建一个新对象的 protocol?为什么 next()
return 具有属性 done
和 value
的新对象,而不是采用像 C# IEnumerable
和 IEnumerator
这样不分配对象的协议以需要两次调用为代价(一次调用 moveNext
以查看迭代是否完成,第二次调用 current
以获取值)?
是否存在跳过 next()
分配对象 return 的底层优化?很难想象,因为 iterable 不知道如何使用一次对象 returned...
生成器似乎不会重用下一个对象,如下图所示:
function* generator() {
yield 0;
yield 1;
}
var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();
console.log(result0.value) // 0
console.log(result1.value) // 1
嗯,here's 一个线索(感谢 Bergi!):
We will answer one important question later (in Sect. 3.2): Why can iterators (optionally) return a value after the last element? That capability is the reason for elements being wrapped. Otherwise, iterators could simply return a publicly defined sentinel (stop value) after the last element.
并且在Sect. 3.2 他们讨论使用 Using generators as lightweight threads。似乎说 return 来自 next
的对象的原因是即使 done
是 true
也可以 return 编辑 value
!哇。此外,除了 yield
和 yield*
值之外,生成器还可以 return
值,并且当 [=17] 时 return
生成的值最终与 value
相同=] 是 true
!
所有这些都允许伪线程。而这个特性,伪线程,值得为每次循环分配一个新对象......Javascript。总是那么出乎意料!
虽然,现在我想起来了,允许 yield*
到 "return" 一个值来启用伪线程仍然不能证明 return 一个对象是合理的。 IEnumerator
协议可以扩展到 return 在 moveNext()
returns false
之后的对象——只需添加一个 属性 hasCurrent
在迭代完成后测试当 true
指示 current
具有有效值时...
并且编译器优化非常重要。这将导致迭代器的性能出现相当大的差异......这不会给库实现者带来问题吗?
所有这些观点都是在 this thread 友好的 SO 社区发现的。然而,这些论点似乎并没有站得住脚。
但是,无论 return 对象与否,没有人会在迭代 "complete" 之后检查值,对吗?例如。大多数人都会认为以下内容会记录迭代器 returned 的所有值:
function logIteratorValues(iterator) {
var next;
while(next = iterator.next(), !next.done)
console.log(next.value)
}
除非它不是因为即使 done
是 false
迭代器 可能仍然有 returned 另一个值。 考虑:
function* generator() {
yield 0;
return 1;
}
var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();
console.log(`${result0.value}, ${result0.done}`) // 0, false
console.log(`${result1.value}, ${result1.done}`) // 1, true
return在其"done"之后有一个值的迭代器真的是迭代器吗?一只手拍手的声音是什么?看起来很奇怪...
并且 here 深入 post 我喜欢的发电机。很多时间花在控制应用程序的流程上,而不是迭代集合的成员。
另一种可能的解释是 IEnumerable/IEnumerator 需要两个接口和三个方法,而 JS 社区更喜欢单一方法的简单性。这样他们就不必引入符号方法组的概念,也就是接口......
Are there under-the-hood optimizations that skip the allocation of the object return by next()
?
是的。这些迭代器结果对象很小,而且通常是短暂的。特别是在 for … of
循环中,编译器可以进行简单的转义分析,以查看该对象根本不面向用户代码(而仅面向内部循环评估代码)。它们可以被垃圾收集器非常有效地处理,甚至可以直接在堆栈上分配。
以下是一些来源:
- JS inherits it functionally-minded iteration protocol from Python, but with results objects instead of the previously favoured
StopIteration
exceptions
- Performance concerns in the spec discussion (cont'd) 被耸了耸肩。如果您实现自定义迭代器并且它太慢,请尝试使用生成器函数
- (至少对于内置迭代器而言)these optimisations are already implemented:
The key to great performance for iteration is to make sure that the repeated calls to iterator.next()
in the loop are optimized well, and ideally completely avoid the allocation of the iterResult
using advanced compiler techniques like store-load propagation, escape analysis and scalar replacement of aggregates. To really shine performance-wise, the optimizing compiler should also completely eliminate the allocation of the iterator
itself - the iterable[Symbol.iterator]()
call - and operate on the backing-store of the iterable directly.
Bergi 已经回答了,我投了赞成票,我只想补充一点:
你为什么还要担心新对象被返回?看起来像:
{done: boolean, value: any}
你知道,无论如何你都会用到value
,所以这真的不是额外的内存开销。还剩什么? done: boolean
和对象本身每个最多占用 8 个字节,这是可能的最小可寻址内存,必须由 cpu 处理并在几皮秒或纳秒内分配到内存中(我认为它是皮秒- 考虑到可能存在的 v8 优化)。现在,如果您仍然关心浪费 那 的时间和内存,那么您真的应该考虑从 JS 切换到 Rust+WebAssembly 之类的东西。
求助!在用 C# 编程了一段时间后,我开始喜欢上 Javascript,但我一直在学习喜欢可迭代协议!
为什么 Javascript 采用需要为每次迭代创建一个新对象的 protocol?为什么 next()
return 具有属性 done
和 value
的新对象,而不是采用像 C# IEnumerable
和 IEnumerator
这样不分配对象的协议以需要两次调用为代价(一次调用 moveNext
以查看迭代是否完成,第二次调用 current
以获取值)?
是否存在跳过 next()
分配对象 return 的底层优化?很难想象,因为 iterable 不知道如何使用一次对象 returned...
生成器似乎不会重用下一个对象,如下图所示:
function* generator() {
yield 0;
yield 1;
}
var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();
console.log(result0.value) // 0
console.log(result1.value) // 1
嗯,here's 一个线索(感谢 Bergi!):
We will answer one important question later (in Sect. 3.2): Why can iterators (optionally) return a value after the last element? That capability is the reason for elements being wrapped. Otherwise, iterators could simply return a publicly defined sentinel (stop value) after the last element.
并且在Sect. 3.2 他们讨论使用 Using generators as lightweight threads。似乎说 return 来自 next
的对象的原因是即使 done
是 true
也可以 return 编辑 value
!哇。此外,除了 yield
和 yield*
值之外,生成器还可以 return
值,并且当 [=17] 时 return
生成的值最终与 value
相同=] 是 true
!
所有这些都允许伪线程。而这个特性,伪线程,值得为每次循环分配一个新对象......Javascript。总是那么出乎意料!
虽然,现在我想起来了,允许 yield*
到 "return" 一个值来启用伪线程仍然不能证明 return 一个对象是合理的。 IEnumerator
协议可以扩展到 return 在 moveNext()
returns false
之后的对象——只需添加一个 属性 hasCurrent
在迭代完成后测试当 true
指示 current
具有有效值时...
并且编译器优化非常重要。这将导致迭代器的性能出现相当大的差异......这不会给库实现者带来问题吗?
所有这些观点都是在 this thread 友好的 SO 社区发现的。然而,这些论点似乎并没有站得住脚。
但是,无论 return 对象与否,没有人会在迭代 "complete" 之后检查值,对吗?例如。大多数人都会认为以下内容会记录迭代器 returned 的所有值:
function logIteratorValues(iterator) {
var next;
while(next = iterator.next(), !next.done)
console.log(next.value)
}
除非它不是因为即使 done
是 false
迭代器 可能仍然有 returned 另一个值。 考虑:
function* generator() {
yield 0;
return 1;
}
var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();
console.log(`${result0.value}, ${result0.done}`) // 0, false
console.log(`${result1.value}, ${result1.done}`) // 1, true
return在其"done"之后有一个值的迭代器真的是迭代器吗?一只手拍手的声音是什么?看起来很奇怪...
并且 here 深入 post 我喜欢的发电机。很多时间花在控制应用程序的流程上,而不是迭代集合的成员。
另一种可能的解释是 IEnumerable/IEnumerator 需要两个接口和三个方法,而 JS 社区更喜欢单一方法的简单性。这样他们就不必引入符号方法组的概念,也就是接口......
Are there under-the-hood optimizations that skip the allocation of the object return by
next()
?
是的。这些迭代器结果对象很小,而且通常是短暂的。特别是在 for … of
循环中,编译器可以进行简单的转义分析,以查看该对象根本不面向用户代码(而仅面向内部循环评估代码)。它们可以被垃圾收集器非常有效地处理,甚至可以直接在堆栈上分配。
以下是一些来源:
- JS inherits it functionally-minded iteration protocol from Python, but with results objects instead of the previously favoured
StopIteration
exceptions - Performance concerns in the spec discussion (cont'd) 被耸了耸肩。如果您实现自定义迭代器并且它太慢,请尝试使用生成器函数
- (至少对于内置迭代器而言)these optimisations are already implemented:
The key to great performance for iteration is to make sure that the repeated calls to
iterator.next()
in the loop are optimized well, and ideally completely avoid the allocation of theiterResult
using advanced compiler techniques like store-load propagation, escape analysis and scalar replacement of aggregates. To really shine performance-wise, the optimizing compiler should also completely eliminate the allocation of theiterator
itself - theiterable[Symbol.iterator]()
call - and operate on the backing-store of the iterable directly.
Bergi 已经回答了,我投了赞成票,我只想补充一点:
你为什么还要担心新对象被返回?看起来像:
{done: boolean, value: any}
你知道,无论如何你都会用到value
,所以这真的不是额外的内存开销。还剩什么? done: boolean
和对象本身每个最多占用 8 个字节,这是可能的最小可寻址内存,必须由 cpu 处理并在几皮秒或纳秒内分配到内存中(我认为它是皮秒- 考虑到可能存在的 v8 优化)。现在,如果您仍然关心浪费 那 的时间和内存,那么您真的应该考虑从 JS 切换到 Rust+WebAssembly 之类的东西。