【问题标题】:Using Objects in For Of Loops在 For Of 循环中使用对象
【发布时间】:2015-07-05 06:50:30
【问题描述】:

为什么不能在 for of 循环中使用对象?或者这是一个浏览器错误?这段代码在 Chrome 42 中不起作用,说 undefined is not a function:

test = { first: "one"}

for(var item of test) {
  console.log(item)
}

【问题讨论】:

  • 测试是数组还是对象?
  • @KickButtowski,你没看到吗?它绝对是一个对象。
  • for (let key of Object.keys(test)) { ... }

标签: javascript for-loop ecmascript-6 iterable


【解决方案1】:

for..of loop 仅支持数组等可迭代对象,不支持对象。

要遍历对象的值,请使用:

for (var key in test) {
    var item = test[key];
}

【讨论】:

  • @DanielHerr 有一个.iterable 成员函数,当您尝试在一个对象(没有它)上使用它时,就会出现错误。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 我的意思是,为什么对象没有那个?原生添加会有什么问题?
  • @DanielHerr 我没有答案,你得问问设计语言的人。
  • @DanielHerr 如果对象“基类”是可迭代的,那么任何函数/日期/等“子类”以及其他复杂性也将是可迭代的。不过,请参阅 esdiscuss.org/topic/es6-iteration-over-object-values#content-5 以更彻底/准确地讨论您的问题。
  • 有了这个 for..in 解决方案,从技术上讲,您是否还需要检查if (test.hasOwnProperty(key)){ ... }?还是不需要?
【解决方案2】:

您可以使用以下语法:

const myObject = {
  first: "one",
  second: "two",
};

for (const [key, value] of Object.entries(myObject)) {
  console.log(key, value);  // first one, second two
}

但是,Object.entries 目前的支持很差 在 IE 或 iOS Safari 中不起作用。您可能可能需要一个 polyfill。最新消息见https://caniuse.com/mdn-javascript_builtins_object_entries

另请参见 Object.keys 仅迭代键,或 Object.values 仅迭代值。

【讨论】:

    【解决方案3】:

    如果您将数据存储在键值存储中,please use Map 专门为此目的而设计。

    如果你必须使用一个对象,ES2017 (ES8) 允许你使用Object.values:

    const foo = { a: 'foo', z: 'bar', m: 'baz' };
    for (let value of Object.values(foo)) {
        console.log(value);
    }
    

    如果还不支持,请使用 polyfill:Alternative version for Object.values()

    最后,如果您支持不支持此语法的旧环境,您将不得不使用forEachObject.keys

    var obj = { a: 'foo', z: 'bar', m: 'baz' };
    Object.keys(obj).forEach(function (prop) {
        var value = obj[prop];
        console.log(value);
    });
    

    【讨论】:

    • 不能扩展对象原型来支持这个吗?
    • @SonicSoul:技术上是的,但通常不建议扩展 Object 原型,因为(几乎)所有东西都继承自它。
    • Object.entries 可以在不接触原型的情况下进行填充。
    • 为什么使用地图而不是对象?
    • 使用这些复杂的例子比简单的for-in 有什么好处吗?
    【解决方案4】:

    ECMAScript 2015/ES6 中的迭代器、可迭代和 for..of 循环

    let tempArray = [1,2,3,4,5];
    
    for(element of tempArray) {
      console.log(element);
    }
    
    // 1
    // 2
    // 3
    // 4
    // 5
    

    但如果我们这样做

    let tempObj = {a:1, b:2, c:3};
    
    for(element of tempObj) {
       console.log(element);
    }
    // error
    

    我们得到错误是因为 for..of 循环仅适用于 Iterables,即具有 @@iterator 的对象遵守 Iterator 协议,这意味着它必须有一个带有 next 方法的对象。下一个方法不带参数,它应该返回一个具有这两个属性的对象。

    done:当为真时表示序列已经结束,假表示可能有更多的值 :这是序列中的当前项

    因此,要使对象 Iterablefor..of 一起工作,我们可以:

    1 . 通过 Symbol.iterator 属性为对象分配神秘的 @@iterator 属性,使对象成为 Iterable。以下是如何:

    let tempObj = {a:1, b:2, c:3};
    
    tempObj[Symbol.iterator]= () => ({
    next: function next () {
    return {
        done: Object.keys(this).length === 0,
        value: Object.keys(this).shift()
         }
        }
      })
    
    for(key in tempObj){
     console.log(key)
    }
    // a
    // b
    // c
    

    2.使用Object.entries,返回一个Iterable

    let tempObj = {a:1, b:2, c:3};
    
    for(let [key, value] of Object.entries(tempObj)) {
        console.log(key, value);
    }
    // a 1
    // b 2
    // c 3
    

    3.使用Object.keys,方法如下:

    let tempObj = {a:1, b:2, c:3};
    for (let key of Object.keys(tempObj)) {
        console.log(key);
    }
    
    // a
    // b
    // c
    

    希望对你有帮助!!!!!!

    【讨论】:

      【解决方案5】:

      我用这段代码使对象可迭代:

      Object.prototype[Symbol.iterator] = function*() {
       for(let key of Object.keys(this)) {
        yield([ key, this[key] ])
      } }
      

      用法:

      for(let [ key, value ] of {}) { }
      

      或者:

      for(let [ key, value ] of Object.entries({})) { }
      

      【讨论】:

      • 不知道为什么这是公认的解决方案。除非它是 polyfill,否则修改原型总是一个糟糕的主意。
      • @user1703761 这是公认的解决方案,因为它有效。请解释如果它如此可怕会导致什么问题。
      • 有各种各样的问题,主要是前向兼容性问题。一个例子是 Array.prototype.includes 之前的名字 contains 但 Moo Tools 扩展了原型并且实现不兼容,请参阅 bugzilla.mozilla.org/show_bug.cgi?id=1075059 也查找原型库 desaster ;)
      • 我相信这不会有前向兼容性问题,因为如果将迭代器添加到对象,这将覆盖它,如果将迭代器添加到对象子类型,它将使用子类型迭代器。
      • 大家好,修改原型是个坏主意!!!让我们为实际给出问题的答案而感到羞耻!
      【解决方案6】:

      因为对象字面量没有Symbol.iterator 属性。具体来说,你只能用for...of循环遍历StringArrayMapSetargumentsNodeList(不广泛支持)和Generator

      要处理 Object Literal 迭代,您有两种选择。

      为...在

      for(let key in obj){
          console.log(obj[key]); 
      }
      

      Object.keys + forEach

      Object.keys(obj).forEach(function(key){
          console.log(obj[key]);
      });
      

      【讨论】:

        【解决方案7】:

        答案是否定的。不可能将 For..Of 与 Object 字面量一起使用。

        我同意 Overv 的观点,即 For..Of 仅适用于可迭代对象。 我有完全相同的问题,因为我使用对象通过 for..in 迭代键和值。但我刚刚意识到这就是 ES6 MAPSSETS 的用途。

        let test = new Map();
        test.set('first', "one");
        test.set('second', "two");
        
        for(var item of test) {
          console.log(item); // "one" "two"
        }
        

        因此它实现了不必使用 for..In(通过 hasOwnProperty 验证)和不必使用 Object.keys() 的目标。

        此外,您的键不限于字符串。您可以使用数字、对象或其他文字。

        【讨论】:

          【解决方案8】:

          对象字面量没有内置的迭代器,这是使用for...of 循环所必需的。但是,如果您不想麻烦地将自己的[Symbol.iterator] 添加到您的对象中,您可以简单地使用Object.keys() 方法。此方法返回一个 Array 对象,该对象已经具有内置迭代器,因此您可以将其与 for...of 循环一起使用,如下所示:

          const myObject = {
              country: "Canada",
              province: "Quebec",
              city: "Montreal"
          }
          
          for (let i of Object.keys(myObject)) {
              console.log("Key:", i, "| Value:", myObject[i]);
          }
          
          //Key: country | Value: Canada
          //Key: province | Value: Quebec
          //Key: city | Value: Montreal
          

          【讨论】:

          • 每次都使用key比添加一次迭代器更麻烦。此外,Object.keys() 是 ES5。
          【解决方案9】:

          可以在任何给定对象上定义迭代器,这样您就可以为每个对象放置不同的逻辑

          var x = { a: 1, b: 2, c: 3 }
          x[Symbol.iterator] = function* (){
              yield 1;
              yield 'foo';
              yield 'last'
          }
          

          那就直接迭代x

          for (let i in x){
              console.log(i);
          }
          //1
          //foo
          //last
          

          可以在Object.prototype 对象上做同样的事情,并为所有对象提供一个通用迭代器

          Object.prototype[Symbol.iterator] = function*() {
              for(let key of Object.keys(this)) {
                   yield key 
              } 
           }
          

          然后像这样迭代你的对象

          var t = {a :'foo', b : 'bar'}
          for(let i of t){
              console.log(t[i]);
          }
          

          或者这样

          var it = t[Symbol.iterator](), p;
          while(p = it.next().value){
              console.log(t[p])
          }
          

          【讨论】:

            【解决方案10】:

            我只是做了以下事情来轻松解决我的问题。

            for (let key in obj) {
              if(obj.hasOwnProperty(key){
                console.log(`${key}: ${obj[key]}`);
              }
            }
            

            【讨论】:

              【解决方案11】:

              使用Object.keys 来获取键数组怎么样?然后forEach on the Array?

              obj = { a: 1, b:2}
              Object.keys(obj).forEach( key => console.log(`${key} => ${obj[key]}`))
              

              【讨论】:

                【解决方案12】:

                如何使用

                function* entries(obj) {
                    for (let key of Object.keys(obj)) {
                        yield [key, obj[key]];
                    }
                }
                
                for ([key, value] of entries({a: "1", b: "2"})) {
                    console.log(key + " " + value);
                }
                

                【讨论】:

                  【解决方案13】:

                  在 ES6 中你可以使用生成器:

                  var obj = {1: 'a', 2: 'b'};
                  
                  function* entries(obj) {
                    for (let key of Object.keys(obj)) {
                      yield [key, obj[key]];
                    }
                  }
                  
                  let generator = entries(obj);
                  
                  let step1 = generator.next();
                  let step2 = generator.next();
                  let step3 = generator.next();
                  
                  console.log(JSON.stringify(step1)); // {"value":["1","a"],"done":false}
                  console.log(JSON.stringify(step2)); // {"value":["2","b"],"done":false}
                  console.log(JSON.stringify(step3)); // {"done":true}
                  

                  Here is the jsfiddle. 在输出中,您将获得一个带有 "value""done" 键的对象。 "Value" 包含您想要它拥有的所有内容,"done" 是布尔迭代的当前状态。

                  【讨论】:

                    【解决方案14】:

                    使用 Array Destruction 您可以使用 forEach 进行如下迭代

                    const obj = { a: 5, b: 7, c: 9 };
                    
                    Object.entries(obj).forEach(([key, value]) => {
                      console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
                    });
                    

                    【讨论】:

                      猜你喜欢
                      • 2020-10-14
                      • 2017-02-08
                      • 1970-01-01
                      • 2020-06-13
                      • 2022-11-23
                      • 2020-07-10
                      • 2018-08-10
                      • 1970-01-01
                      • 2016-03-27
                      相关资源
                      最近更新 更多