为什么不是对象的Iterable在JavaScript中? [英] Why are Objects not Iterable in JavaScript?

查看:1188
本文介绍了为什么不是对象的Iterable在JavaScript中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么对象不是默认迭代?

我看到的问题,一切以迭代对象有关时,常见的解决方案是迭代某对象的属性和访问对象中的值的方式。这似乎是很常见的,它使我不知道为什么对象本身不是可迭代的。

之类的语句的ES6 <一个href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of\"><$c$c>for...of将是很好默认使用的对象。因为这些功能仅适用于特殊的可迭代的对象,这不包括 {} 的对象,我们不得不通过箍,使这项工作的对象,我们要使用它。


  

对于在...的语句创建一个循环遍历迭代对象
  (包括阵列,地图,设置,参数对象等)...


例如使用ES6 <一个href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*\">generator功能:

  VAR例如= {A:{E:一,F:两节},B:{G:'三'},C:{H:四我:十二五}};功能*条目(OBJ){
   对于(让Object.keys(OBJ)的键){
     产量[键,OBJ [关键]];
   }
}对于(让[键,值]条目(例如)){
  的console.log(键);
  的console.log(值);
  对于(让[键,值]条目(值)){
    的console.log(键);
    的console.log(值);
  }
}

以上正常登录我预计订单数据当我运行在Firefox中code(支持<一到href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla\">ES6):

默认情况下, {} 对象不是可迭代的,但是为什么呢?请问缺点超过对象是可迭代的潜在收益?什么是与此相关的问题是什么?

另外,由于 {} 对象来自阵列状集合和可迭代的对象,如的 节点列表 ,<一个href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection\"><$c$c>HtmlCollection,和<一个href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\"><$c$c>arguments,它们不能被转换成阵列

例如:

VAR argumentsArray = Array.prototype.slice.call(参数);

或阵列的方法中使用:

Array.prototype.forEach.call(节点列表,功能(元素){})

除了我上面的问题,我希望能看到关于如何使工作示例 {} 对象到iterables,尤其是从谁提到的那些在 [Symbol.iterator] 这应该允许这些新的 {} 可迭代的对象来使用之类的语句 ...。另外,我不知道是否使物体迭代让他们转换成数组。

我试过低于code,但我得到一个类型错误:不能转换未定义的对象

  VAR例如= {A:{E:一,F:两节},B:{G:'三'},C:{H:四我:十二五}};//我希望能够用为......为榜样的对象。
//我也希望能够在榜样的对象转换为数组。
例如[Symbol.iterator] =功能*(OBJ){
   对于(让Object.keys(OBJ)的键){
     产量[键,OBJ [关键]];
   }
};对于(让[键,值]例子){执行console.log(值); } //错误
的console.log([...示例]); //错误


解决方案

我会给这是一个尝试。请注意,我不隶属于ECMA并没有了解其决策过程,所以我不能肯定地说的为什么的他们还是没有做任何事情。不过,我会说出我的假设,并把我最好的拍摄。

1。为什么要添加为...的首先构建?

JavaScript的已经包括为中... 构造可用于迭代一个对象的属性。然而,这不是真的foreach循环,因为它枚举所有属性的对象,而且往往只工作$ P在简单的情况下pdictably $。

有分解在更复杂的情况下(包括与阵列,其中,它的使用趋向于任气馁或数组彻底模糊通过在为...保障>的正确的)。您可以解决通过使用的hasOwnProperty (除其他事项外),但是这是一个有点笨拙和不雅。

因此​​,因此我的假设是,为...的构造被添加到解决与相关联的不足之处......在结构,和迭代东西的时候提供更大的实用性和灵活性。人们往往把为中... 的forEach 循环,可普遍适用于任何收集和生产理智导致在任何可能的情况下,但是这不会发生什么变化。在为...的循环修复了。

我也认为它是对现有ES5 code到ES6下运行,产生相同的结果,因为它没有ES5下,这样重大更改无法进行,例如重要的是,在<$ C $的行为C>的...在结构。

2。如何为...的工作?

的<一个href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols\">reference文档是这部分很有用。具体而言,物体被认为是迭代如果定义了 Symbol.iterator 属性。

的属性定义应返回的项目的集合,一个在由一个,并设置一个标志指示是否有更多的项目来获取的功能。提供了一些对象类型的,而且,使用的是比较明确的。 ..of 简单地委托给迭代器功能。

此方法非常有用,因为它使得它非常简单的提供自己的迭代器。我可以说的办法可以有presented实际问题,由于其在定义属性的依赖,其中previously那里没有,除了从我可以告诉大家,这并非如此,因为新的属性基本上是忽略,除非你故意去寻找它(也就是说,它不会在为... present在循环作为重点,等等)。所以这是不是这样的。

实用非问题放在一边,它可能已被认为是有争议的概念开始所有新的pre定义的属性对象关,或含蓄地说,每一个对象是一个集​​合。

3。为什么不使用迭代对象为... 的默认?

我的的是,这是一个组合:


  1. 使所有对象迭代默认情况下可能被认为是不可接受的,因为它增加了,其中previously有没有财产,或因为对象不是(不一定)的集合。随着菲利克斯指出,这是什么意思迭代函数或一个普通的前pression对象?

  2. 简单对象已经可以使用为中... 迭代,这不是清楚内置迭代器实现可以做不同/比现有的更好为中... 行为。所以,即使#1是错误的,所添加的属性是可以接受的,它可能不会被视为的有用

  3. 谁不想让自己的对象的用户迭代可以很容易地做到这一点,通过定义 Symbol.iterator 属性。

  4. 的ES6规范还提供导航类型,它迭代默认情况下,并且使用普通的对象有一些小优点地图

甚至还有的参考文档中提供了3#一个例子:

  VAR myIterable = {};
myIterable [Symbol.iterator] =功能*(){
    产量1;
    产量2;
    产量3;
};对于(myIterable的VAR值){
    的console.log(值);
}

由于对象可以很容易地进行迭代,他们可以使用已经进行迭代为中... ,那里面很可能不是在默认对象迭代器应该做的事情达成明确的协议(如果它做什么,就是要由什么为中... 确实有些不同)它似乎很合理的对象是默认情况下不发迭代

请注意,您的例子code可以使用重写为中...

 的(让对象levelOneKey){
    的console.log(levelOneKey); //榜样
    的console.log(对象[levelOneKey]); // {随机:鸟巢,另一个:东西}    VAR levelTwoObj =对象[levelOneKey]
    对(让levelTwoObj levelTwoKey){
        的console.log(levelTwoKey); //随机
        的console.log(levelTwoObj [levelTwoKey]); //鸟巢
    }
}

...或者你也可以让你的对象迭代你想要做类似下面(或者你可以做的所有对象迭代的Object.prototype> [Symbol.iterator] 代替):

  OBJ = {
    答:1,
    乙:{一句:别的},
    C:4,
    D:{嵌套:{nestedAgain:真正}}
};OBJ [Symbol.iterator] =功能(){
    变种键= [];
    VAR REF =这一点;
    为(在此变种键){
        //注:可以做的hasOwnProperty()在这里等。
        keys.push(键);
    }    返回{
        下一篇:函数(){
            如果(this._keys&安培;&安培; this._obj和放大器;&安培; this._index&LT; this._keys.length){
                VAR键= this._keys [this._index]
                this._index ++;
                返回{键:键,值:this._obj [键],来完成:假};
            }其他{
                返回{做到:真​​正};
            }
        },
        _index:0,
        _keys文件:钥匙,
        _obj:REF
    };
};

您可以在这里玩(在Chrome中,在租赁): http://jsfiddle.net/rncr3ppz/5/

修改

和回应更新的问题,是的,有可能的迭代转换为数组,使用s$p$pad在ES6运营商

不过,这似乎并没有在Chrome中工作呢,至少我不能让它在我的jsfiddle工作。从理论上讲它应该是简单的:

  VAR阵列= [... myIterable]

Why are objects not iterable by default?

I see questions all the time related to iterating objects, the common solution being to iterate over an object's properties and accessing the values within an object that way. This seems so common that it makes me wonder why objects themselves aren't iterable.

Statements like the ES6 for...of would be nice to use for objects by default. Because these features are only available for special "iterable objects" which don't include {} objects, we have to go through hoops to make this work for objects we want to use it for.

The for...of statement creates a loop Iterating over iterable objects (including Array, Map, Set, arguments object and so on)...

For example using an ES6 generator function:

var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};

function* entries(obj) {
   for (let key of Object.keys(obj)) {
     yield [key, obj[key]];
   }
}

for (let [key, value] of entries(example)) {
  console.log(key);
  console.log(value);
  for (let [key, value] of entries(value)) {
    console.log(key);
    console.log(value);
  }
}

The above properly logs data in the order I expect it to when I run the code in Firefox (which supports ES6):

By default, {} objects are not iterable, but why? Would the disadvantages outweigh the potential benefits of objects being iterable? What are the issues associated with this?

In addition, because {} objects are different from "Array-like" collections and "iterable objects" such as NodeList, HtmlCollection, and arguments, they can't be converted into Arrays.

For example:

var argumentsArray = Array.prototype.slice.call(arguments);

or be used with Array methods:

Array.prototype.forEach.call(nodeList, function (element) {}).

Besides the questions I have above, I would love to see a working example on how to make {} objects into iterables, especially from those who have mentioned the [Symbol.iterator]. This should allow these new {} "iterable objects" to use statements like for...of. Also, I wonder if making objects iterable allow them to be converted into Arrays.

I tried the below code, but I get a TypeError: can't convert undefined to object.

var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};

// I want to be able to use "for...of" for the "example" object.
// I also want to be able to convert the "example" object into an Array.
example[Symbol.iterator] = function* (obj) {
   for (let key of Object.keys(obj)) {
     yield [key, obj[key]];
   }
};

for (let [key, value] of example) { console.log(value); } // error
console.log([...example]); // error

解决方案

I'll give this a try. Note that I'm not affiliated with ECMA and have no visibility into their decision-making process, so I cannot definitively say why they have or have not done anything. However, I'll state my assumptions and take my best shot.

1. Why add a for...of construct in the first place?

JavaScript already includes a for...in construct that can be used to iterate the properties of an object. However, it's not really a forEach loop, as it enumerates all of the properties on an object and tends to only work predictably in simple cases.

It breaks down in more complex cases (including with arrays, where its use tends to be either discouraged or thoroughly obfuscated by the safeguards needed to for use for...in with an array correctly). You can work around that by using hasOwnProperty (among other things), but that's a bit clunky and inelegant.

So therefore my assumption is that the for...of construct is being added to address the deficiencies associated with the for...in construct, and provide greater utility and flexibility when iterating things. People tend to treat for...in as a forEach loop that can be generally applied to any collection and produce sane results in any possible context, but that's not what happens. The for...of loop fixes that.

I also assume that it's important for existing ES5 code to run under ES6 and produce the same result as it did under ES5, so breaking changes cannot be made, for instance, to the behavior of the for...in construct.

2. How does for...of work?

The reference documentation is useful for this part. Specifically, an object is considered iterable if it defines the Symbol.iterator property.

The property-definition should be a function that returns the items in the collection, one, by, one, and sets a flag indicating whether or not there are more items to fetch. Predefined implementations are provided for some object-types, and it's relatively clear that using for...of simply delegates to the iterator function.

This approach is useful, as it makes it very straightforward to provide your own iterators. I might say the approach could have presented practical issues due to its reliance upon defining a property where previously there was none, except from what I can tell that's not the case as the new property is essentially ignored unless you deliberately go looking for it (i.e. it will not present in for...in loops as a key, etc.). So that's not the case.

Practical non-issues aside, it may have been considered conceptually controversial to start all objects off with a new pre-defined property, or to implicitly say that "every object is a collection".

3. Why are objects not iterable using for...of by default?

My guess is that this is a combination of:

  1. Making all objects iterable by default may have been considered unacceptable because it adds a property where previously there was none, or because an object isn't (necessarily) a collection. As Felix notes, "what does it mean to iterate over a function or a regular expression object"?
  2. Simple objects can already be iterated using for...in, and it's not clear what a built-in iterator implementation could have done differently/better than the existing for...in behavior. So even if #1 is wrong and adding the property was acceptable, it may not have been seen as useful.
  3. Users who want to make their objects iterable can easily do so, by defining the Symbol.iterator property.
  4. The ES6 spec also provides a Map type, which is iterable by default and has some other small advantages over using a plain object as a Map.

There's even an example provided for #3 in the reference documentation:

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

for (var value of myIterable) {
    console.log(value);
}

Given that objects can easily be made iterable, that they can already be iterated using for...in, and that there's likely not clear agreement on what a default object iterator should do (if what it does is meant to be somehow different from what for...in does), it seems reasonable enough that objects were not made iterable by default.

Note that your example code can be rewritten using for...in:

for (let levelOneKey in object) {
    console.log(levelOneKey);         //  "example"
    console.log(object[levelOneKey]); // {"random":"nest","another":"thing"}

    var levelTwoObj = object[levelOneKey];
    for (let levelTwoKey in levelTwoObj ) {
        console.log(levelTwoKey);   // "random"
        console.log(levelTwoObj[levelTwoKey]); // "nest"
    }
}

...or you can also make your object iterable in the way you want by doing something like the following (or you can make all objects iterable by assigning to Object.prototype[Symbol.iterator] instead):

obj = { 
    a: '1', 
    b: { something: 'else' }, 
    c: 4, 
    d: { nested: { nestedAgain: true }}
};

obj[Symbol.iterator] = function() {
    var keys = [];
    var ref = this;
    for (var key in this) {
        //note:  can do hasOwnProperty() here, etc.
        keys.push(key);
    }

    return {
        next: function() {
            if (this._keys && this._obj && this._index < this._keys.length) {
                var key = this._keys[this._index];
                this._index++;
                return { key: key, value: this._obj[key], done: false };
            } else {
                return { done: true };
            }
        },
        _index: 0,
        _keys: keys,
        _obj: ref
    };
};

You can play with that here (in Chrome, at lease): http://jsfiddle.net/rncr3ppz/5/

Edit

And in response to your updated question, yes, it is possible to convert an iterable to an array, using the spread operator in ES6.

However, this doesn't seem to be working in Chrome yet, or at least I cannot get it to work in my jsFiddle. In theory it should be as simple as:

var array = [...myIterable];

这篇关于为什么不是对象的Iterable在JavaScript中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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