为什么对象不能在JavaScript中迭代?

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

我看到问题所有的时间与迭代对象相关,常见的解决方案是迭代对象的属性和访问对象的方式。这似乎很常见,它让我想知道为什么对象本身不可迭代。

默认情况下,类似ES6 for...of的语句很适合用于对象。因为这些特性只适用于不包括{}对象的特殊“可迭代对象”,所以我们必须经历一些圈子,使其适用于我们想要使用的对象。

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

例如,使用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);
  }
}

上面的正确记录数据,我期望它的顺序,当我在Firefox中运行代码(支持ES6):

默认情况下,{}对象不可迭代,但为什么?这些缺点会超过对象可迭代的潜在好处吗?与此相关的是什么问题?

此外,因为{}对象不同于“类似Array”的集合和“可迭代对象”,例如NodeList,HtmlCollectionarguments,所以不能将它们转换为数组。

例如:

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

或者与Array方法一起使用:

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

除了上面的问题,我很想看到一个工作示例,如何使{}对象成为可迭代,特别是那些谁提到了[Symbol.iterator]。这应该允许这些新的{}“可迭代对象”使用像…的语句。此外,我不知道如果使对象可迭代允许将它们转换为数组。

我试过下面的代码,但我得到一个TypeError:不能转换为未定义对象。

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
我会给这个尝试。请注意,我不隶属于ECMA,也没有可见性的决策过程,所以我不能确定地说,为什么他们有或没有做任何事情。但是,我会说明我的假设,并采取我的最佳拍摄。

为什么添加一个for …的构造在第一位?

JavaScript在构造中已经包含了一个for …,可以用来迭代对象的属性。然而,它是not really a forEach loop,因为它枚举了一个对象上的所有属性,往往只能在简单的情况下可预测的工作。

它在更复杂的情况下崩溃(包括使用数组,其中它的使用往往是discouraged or thoroughly obfuscated通过安全措施需要使用正确的数组)。你可以通过使用hasOwnProperty(除了别的以外)解决这个问题,但是这有点笨重和不起眼。

因此,我的假设是,构造的for …被添加来解决与for …在构造中的缺陷,并在迭代的时候提供更大的效用和灵活性。人们倾向于把for …作为一个forEach循环来处理,它通常可以应用于任何集合,并在任何可能的上下文中产生可靠的结果,但这不是发生的。 for … for循环修复了。

我还假定现有的ES5代码在ES6下运行和产生与在ES5下相同的结果很重要,因此不能对构造中的for … in的行为进行破坏性更改。

2.如何工作?

reference documentation对这部分很有用。具体来说,如果一个对象定义了Symbol.iterator属性,则它被认为是可迭代的。

属性定义应该是一个函数,返回集合中的项目,一,乘一,并设置一个标志,指示是否有更多的项目要获取。为some object-types提供了预定义的实现,并且相对清楚的是,使用for …只是委托给迭代器函数。

这种方法很有用,因为它使提供您自己的迭代器变得非常简单。我可能会说,该方法可能会提出实际的问题,由于它依赖于定义一个属性,以前没有,除了我可以告诉这不是这种情况,因为新的属性基本上被忽略,除非你故意去寻找它(即它不会出现在for … in循环作为一个键,等等)。所以不是这样的。

除了实际的非问题之外,可能已被认为具有概念上的争议,以使用新的预定义属性来启动所有对象,或者隐含地说“每个对象是集合”。

为什么对象不可迭代使用默认情况下的for …?

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

>使默认可迭代的所有对象可能被认为是不可接受的,因为它添加了一个以前没有的属性,或者因为一个对象不一定是一个集合。正如Felix所说,“迭代一个函数或一个正则表达式对象是什么意思?
>简单对象可以使用for … in已经迭代,并且不清楚内置迭代器实现可以做不同/更好的行为中的现有的…。因此,即使#1是错误的,添加属性是可以接受的,它可能不被视为有用。
>想要使其对象可迭代的用户可以通过定义Symbol.iterator属性来轻松实现。
> ES6规范还提供了一个Map类型,默认情况下是可迭代的,并且与使用简单对象作为Map相比具有一些其他小的优点。

还有一个为参考文档中的#3提供的示例:

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

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

给定对象可以很容易地做可迭代,它们可以已经使用for … in迭代,并且可能没有明确的一致默认对象迭代器应该做什么(如果它做的是什么意思与某种程度上不同于什么for … in),似乎很合理,对象不是默认可迭代的。

请注意,您的示例代码可以使用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"
    }
}

…或者你也可以通过做一些像下面这样的事情使你的对象可迭代(或者你可以通过分配Object.prototype [Symbol.iterator]来使所有对象迭代):

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
    };
};

您可以在这里玩(在Chrome,at lease):http://jsfiddle.net/rncr3ppz/5/

编辑

并且响应您更新的问题,是的,可以使用ES6中的spread operator将可迭代转换为数组。

但是,这似乎还没有在Chrome中工作,或者至少我不能让它工作在我的jsFiddle。理论上它应该像下面这样简单:

var array = [...myIterable];
http://stackoverflow.com/questions/29886552/why-are-objects-not-iterable-in-javascript

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:为什么对象不能在JavaScript中迭代?