gpt4 book ai didi

javascript - 为什么在JavaScript中对象不可迭代?

转载 作者:行者123 更新时间:2023-12-02 00:51:08 25 4
gpt4 key购买 nike

为什么默认情况下无法迭代对象?

我经常看到与迭代对象有关的问题,常见的解决方案是迭代对象的属性并以这种方式访问​​对象中的值。这似乎很常见,使我想知道为什么对象本身不可迭代。

默认情况下,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)中运行代码时期望的顺序正确记录了数据:

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

另外,由于 {}对象与 NodeList HtmlCollection arguments 之类的“类似数组的”集合和“可迭代对象”不同,因此无法将其转换为数组。

例如:
var argumentsArray = Array.prototype.slice.call(arguments);
或与Array方法一起使用:
Array.prototype.forEach.call(nodeList, function (element) {})

除了上面的问题之外,我还想看到一个有效的示例,说明如何将{}对象转换为可迭代对象,尤其是那些提到了[Symbol.iterator]的对象。 这应该允许这些新的 {}“可迭代对象”使用类似 for...of的语句。此外,我想知道是否使对象可迭代允许它们转换为数组。

我尝试了以下代码,但得到了 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

最佳答案

我会尝试的。请注意,我不隶属于ECMA,并且对他们的决策过程不了解,所以我不能确切地说出为什么他们没有做任何事情。但是,我会陈述自己的假设并尽我最大的努力。

1.为什么首先要添加for...of构造?

JavaScript已经包含for...in构造,可用于迭代对象的属性。但是,它是not really a forEach loop,因为它枚举了对象上的所有属性,并且仅在可预见的情况下才能正常工作。

它在更复杂的情况下会崩溃(包括在数组中,由于正确地将for...in与数组一起使用所需的防护措施,其使用往往是discouraged or thoroughly obfuscated)。您可以使用hasOwnProperty(除其他方法外)解决此问题,但这有点笨拙且不雅致。

因此,因此我的假设是添加了for...of构造以解决与for...in构造相关的缺陷,并在迭代时提供更大的实用性和灵活性。人们倾向于将for...in视为forEach循环,该循环通常可以应用于任何集合,并在任何可能的上下文中产生理智的结果,但是事实并非如此。 for...of循环可解决此问题。

我还假设对于现有的ES5代码来说,在ES6下运行并产生与ES5下相同的结果非常重要,因此无法对for...in构造的行为进行重大更改。

2. for...of如何工作?

reference documentation对于此部分很有用。具体来说,如果对象定义了iterable属性,则将其视为Symbol.iterator

属性定义应该是一个函数,该函数一个接一个地返回集合中的项目,并设置一个标志,该标志指示是否还有更多要提取的项目。为some object-types提供了预定义的实现,并且很明显,使用for...of可以简单地委派给迭代器函数。

这种方法很有用,因为它使提供自己的迭代器变得非常简单。我可能会说,由于依赖于定义以前没有属性的属性,因此该方法可能会带来实际问题,但我可以说不是这样,因为除非您有意去寻找它,否则新属性实际上会被忽略(即它不会作为关键字出现在for...in循环中)。事实并非如此。

除了实用的非问题,从概念上讲,以新的预定义属性开始所有对象,或隐式地说“每个对象都是一个集合”在概念上是有争议的。

3.为什么默认情况下对象不使用iterable进行for...of

我的猜测是这是以下各项的组合:

  • 将所有对象默认设置为iterable可能被认为是 Not Acceptable ,因为它在以前没有属性的地方添加了一个属性,或者因为(不一定)对象不是一个集合。正如Felix所说,“遍历函数或正则表达式对象意味着什么?”
  • 已经可以使用for...in迭代简单的对象,尚不清楚内置迭代器实现与现有for...in行为相比有何不同/更好。因此,即使#1错误并且添加了该属性是可以接受的,它也可能未被认为有用。
  • 通过定义iterable属性,想要使自己的对象成为Symbol.iterator的用户可以轻松实现。
  • ES6规范还提供了Map类型,默认情况下为iterable,与使用普通对象作为Map相比,还有其他一些小优点。

  • 引用文档中甚至为#3提供了一个示例:
    var myIterable = {};
    myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
    };

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

    鉴于可以轻松地将对象设置为 iterable,可以使用 for...in对其进行迭代,并且对于默认对象迭代器应该做什么(可能与 for...in有所不同)尚无明确的共识,似乎足够合理,默认情况下不将对象设置为 iterable

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

    ...或者您也可以通过执行以下操作来以所需的方式使对象 iterable成为可能(或者,也可以通过分配给 iterable来使所有对象成为 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浏览器中租借)使用它: http://jsfiddle.net/rncr3ppz/5/

    编辑

    回答您的更新问题,是的,可以使用ES6中的 spread operatoriterable转换为数组。

    但是,这似乎在Chrome中尚不可用,或者至少我无法在jsFiddle中使用它。从理论上讲,它应该很简单:
    var array = [...myIterable];

    关于javascript - 为什么在JavaScript中对象不可迭代?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59363620/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com