为什么 Javascript `iterator.next()` 返回一个对象? [英] Why does Javascript `iterator.next()` return an object?

查看:83
本文介绍了为什么 Javascript `iterator.next()` 返回一个对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

帮助!在用 C# 编程一段时间后,我开始学习喜欢 Javascript,但我一直在学习喜欢可迭代协议!

为什么 Javascript 采用协议 需要为每次迭代创建一个新对象?为什么 next() 返回一个具有 donevalue 属性的新对象,而不是采用像 C# IEnumerable 这样的协议和 IEnumerator 不分配任何对象,代价是需要两次调用(一次到 moveNext 以查看迭代是否完成,第二次到 current> 获取值)?

是否存在跳过 next() 返回的对象分配的底层优化?很难想象,因为可迭代对象不知道返回后如何使用对象...

生成器似乎不会重用下一个对象,如下图所示:

function* generator() {产量 0;产量 1;}var iterator = generator();var result0 = iterator.next();var result1 = iterator.next();console.log(result0.value)//0console.log(result1.value)//1

嗯,这里是一个线索(感谢 Bergi!):

<块引用>

我们稍后会回答一个重要问题(在第 3.2 节):为什么迭代器(可选)可以在最后一个元素之后返回一个值?这种能力是元素被包装的原因.否则,迭代器可以简单地在最后一个元素之后返回一个公开定义的标记(停止值).

在教派中.3.2 他们讨论使用使用生成器作为轻量级线程.似乎说从 next 返回一个对象的原因是为了即使 donetrue 也可以返回一个 value代码>!哇.此外,除了 yieldyield*-ing 值和由 return 生成的值之外,生成器还可以 return 值当 donetrue 时,以 value 结束!

所有这些都允许伪线程.而这个特性,伪线程,值得为每次循环分配一个新对象......Javascript.总是那么出乎意料!

<小时>

虽然,现在我考虑了一下,允许 yield* 返回"一个值以启用伪线程仍然不能证明返回一个对象是合理的.IEnumerator 协议可以扩展为在 moveNext() 返回 false 后返回一个对象——只需添加一个属性 hasCurrent> 在迭代完成后测试当 true 表示 current 具有有效值时...

而且编译器优化非常重要.这将导致迭代器的性能出现相当大的差异......这不会给库实现者带来问题吗?

所有这些观点都在这个主题 由友好的 SO 社区发现.然而,这些争论似乎站不住脚.

<小时>

然而,无论是否返回一个对象,在迭代完成"之后,没有人会检查一个值,对吧?例如.大多数人都会认为以下内容会记录迭代器返回的所有值:

function logIteratorValues(iterator) {下一个变量;while(next = iterator.next(), !next.done)控制台日志(下一个值)}

除非它没有,因为即使 donefalse,迭代器仍然可能返回另一个值. 考虑:

function* generator() {产量 0;返回 1;}var iterator = generator();var result0 = iterator.next();var result1 = iterator.next();console.log(`${result0.value}, ${result0.done}`)//0, falseconsole.log(`${result1.value}, ${result1.done}`)//1, true

完成"后返回值的迭代器真的是迭代器吗?一只手拍手的声音是什么?只是看起来很奇怪...

<小时>

这里是关于我喜欢的生成器的深入帖子.与迭代集合的成员相比,很多时间都花在了控制应用程序的流程上.

<小时>

另一种可能的解释是 IEnumerable/IEnumerator 需要两个接口和三个方法,而 JS 社区更喜欢单一方法的简单性.这样他们就不必引入符号方法组又名接口的概念......

解决方案

是否存在跳过 next() 返回的对象分配的底层优化?

是的.那些迭代器结果对象很小并且通常是短暂的.特别是在 for ... of 循环中,编译器可以做一个简单的转义分析来查看对象根本不面对用户代码(而只面对内部循环评估代码).它们可以由垃圾收集器非常有效地处理,甚至可以直接在堆栈上分配.

以下是一些来源:

Help! I'm learning to love Javascript after programming in C# for quite a while but I'm stuck learning to love the iterable protocol!

Why did Javascript adopt a protocol that requires creating a new object for each iteration? Why have next() return a new object with properties done and value instead of adopting a protocol like C# IEnumerable and IEnumerator which allocates no object at the expense of requiring two calls (one to moveNext to see if the iteration is done, and a second to current to get the value)?

Are there under-the-hood optimizations that skip the allocation of the object return by next()? Hard to imagine given the iterable doesn't know how the object could be used once returned...

Generators don't seem to reuse the next object as illustrated below:

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

Hm, here's a clue (thanks to 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.

And in Sect. 3.2 they discuss using Using generators as lightweight threads. Seems to say the reason for return an object from next is so that a value can be returned even when done is true! Whoa. Furthermore, generators can return values in addition to yield and yield*-ing values and a value generated by return ends up as in value when done is true!

And all this allows for pseudo-threading. And that feature, pseudo-threading, is worth allocating a new object for each time around the loop... Javascript. Always so unexpected!


Although, now that I think about it, allowing yield* to "return" a value to enable a pseudo-threading still doesn't justify returning an object. The IEnumerator protocol could be extended to return an object after moveNext() returns false -- just add a property hasCurrent to test after the iteration is complete that when true indicates current has a valid value...

And the compiler optimizations are non-trivial. This will result in quite wild variance in the performance of an iterator... doesn't that cause problems for library implementors?

All these points are raised in this thread discovered by the friendly SO community. Yet, those arguments didn't seem to hold the day.


However, regardless of returning an object or not, no one is going to be checking for a value after iteration is "complete", right? E.g. most everyone would think the following would log all values returned by an iterator:

function logIteratorValues(iterator) {
  var next;
  while(next = iterator.next(), !next.done)
    console.log(next.value)
}

Except it doesn't because even though done is false the iterator might still have returned another value. Consider:

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

Is an iterator that returns a value after its "done" is really an iterator? What is the sound of one hand clapping? It just seems quite odd...


And here is in depth post on generators I enjoyed. Much time is spent controlling the flow of an application as opposed to iterating members of a collection.


Another possible explanation is that IEnumerable/IEnumerator requires two interfaces and three methods and the JS community preferred the simplicity of a single method. That way they wouldn't have to introduce the notion of groups of symbolic methods aka interfaces...

解决方案

Are there under-the-hood optimizations that skip the allocation of the object return by next()?

Yes. Those iterator result objects are small and usually short-lived. Particularly in for … of loops, the compiler can do a trivial escape analysis to see that the object doesn't face the user code at all (but only the internal loop evaluation code). They can be dealt with very efficiently by the garbage collector, or even be allocated directly on the stack.

Here are some sources:

这篇关于为什么 Javascript `iterator.next()` 返回一个对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆