开发者社区 问答 正文

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

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

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

for...of默认情况下,像ES6 这样的语句很适合用于对象。因为这些功能仅适用于不包含{}对象的特殊“可迭代对象” ,所以我们必须仔细研究以使其适用于要用于它的对象。

for ... of语句创建一个循环,遍历可迭代对象 (包括Array,Map,Set,arguments对象等)...

例如,使用ES6 生成器函数:

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

hacky的输出...

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

此外,由于{}对象是从“阵列状”集合和不同的“可迭代对象”,如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 问题来源于stack overflow

展开
收起
保持可爱mmm 2020-02-08 20:02:49 2414 分享 版权
1 条回答
写回答
取消 提交回答
  • 我会尝试的。请注意,我不隶属于ECMA,并且对他们的决策过程不了解,所以我不能确切地说出他们为什么做或没有做任何事情。但是,我会陈述自己的假设并尽我所能。

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

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

    它在更复杂的情况下会崩溃(包括在数组中,使用正确的数组所需要的保护措施会阻止或彻底混淆其使用)。您可以使用(除其他方法外)解决此问题,但这有点笨拙且不雅致。 for...inhasOwnProperty

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

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

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

    的参考文档是对于这部分是有用的。具体来说,如果对象iterable定义了Symbol.iterator属性,则将其视为对象。

    属性定义应该是一个函数,该函数一个接一个地返回集合中的项目,并设置一个标志,指示是否还有更多要提取的项目。为某些对象类型提供了预定义的实现,并且很显然,for...of仅使用委托给迭代器函数的代理即可。

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

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

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

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

    iterable默认情况下,使所有对象成为默认对象是不可接受的,因为它在以前没有属性的地方添加了一个属性,或者因为(不是)对象不是一个集合。正如Felix所说,“遍历函数或正则表达式对象意味着什么?” 简单的对象已经可以使用进行迭代for...in,并且尚不清楚内置迭代器实现与现有for...in行为可以做的不同/更好。因此,即使#1错误并且添加了属性是可以接受的,也可能未将其视为有用。 想要创建其对象的iterable用户可以通过定义Symbol.iterator属性轻松地做到这一点。 所述ES6规范还提供了一个地图类型,它是 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通过执行以下操作来以所需的方式创建对象(或者可以通过分配给来创建所有对象):iterableObject.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/

    2020-02-08 20:03:06
    赞同 展开评论