草庐IT

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

coder 2023-07-03 原文

为什么默认情况下对象不可迭代?

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

类似 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属性(property)。

属性定义应该是一个函数,它返回集合中的项目,一个,一个,一个,并设置一个标志,指示是否还有更多的项目要获取。为 some object-types 提供了预定义的实现,而且使用 for...of 比较清楚简单地委托(delegate)给迭代器函数。

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

撇开实际的非问题不谈,以新的预定义属性开始所有对象,或隐含地说“每个对象都是一个集合”,这可能在概念上被认为是有争议的。

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

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

  • 制作所有对象 iterable默认情况下可能被认为是 Not Acceptable ,因为它添加了一个以前没有的属性,或者因为对象不是(必然)集合。正如 Felix 所指出的,“迭代一个函数或一个正则表达式对象是什么意思”?
  • 简单对象已经可以使用 for...in 进行迭代,并且不清楚内置的迭代器实现可以比现有的 for...in 做得不同/更好。行为。因此,即使 #1 是错误的并且添加属性是可以接受的,它也可能不被视为有用。
  • 想要制作对象的用户 iterable可以很容易地做到这一点,通过定义 Symbol.iterator属性(property)。
  • 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 所做的有所不同)可能没有明确的协议(protocol),似乎很合理,对象不是 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/

    编辑

    针对您更新的问题,是的,可以转换 iterable到一个数组,使用 spread operator在 ES6 中。

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

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

    有关javascript - 为什么对象在 JavaScript 中不可迭代?的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    4. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

      在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

    5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    7. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    8. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

      我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

    9. Ruby 写入和读取对象到文件 - 2

      好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

    10. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

      它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

    随机推荐