一个Javascript对象如何变成可迭代的,以便在for...of语句中使用?

20

我想设置 options[Symbol.iterator] 属性,以便在使用 for...of 语句迭代我创建的简单对象:

我希望能够通过设置 options[Symbol.iterator] 属性来让我创建的简单对象能够被 for...of 语句迭代:

options = {
  male: 'John',
  female: 'Gina',
  rel: 'Love'
};


for(let p of options){
  console.log(`Property ${p}`);
};

但是这段代码给我以下错误:

 array.html:72 Uncaught TypeError: options[Symbol.iterator] is not a function

如何在上述简单对象上设置正确的迭代器函数?

已解决

 // define the Iterator for the options object 
 options[Symbol.iterator] = function(){

     // get the properties of the object 
     let properties = Object.keys(this);
     let count = 0;
     // set to true when the loop is done 
     isDone = false;

     // define the next method, need for iterator 
     let next = () => {
        // control on last property reach 
        if(count >= properties.length){
           isDone = true;
        }
        return {done:isDone, value: this[properties[count++]]};
     }

     // return the next method used to iterate 
     return {next};
  };

现在我可以在可迭代对象上使用for...of语句:

 for(let property of options){
   console.log(`Properties -> ${property}`);
 }

2
要使用 for...of 循环,集合必须具有 [Symbol.iterator] 属性。对象字面量没有这样的属性,只有数组、集合、映射等才有。 - adeneo
你想要使用一个Map - Bergi
3个回答

32

使用 for...of 循环时,您需要使用 [Symbol.iterator] 键为对象定义一个适当的迭代器。

以下是一种可能的实现方式:

let options = {
  male: 'John',
  female: 'Gina',
  rel: 'Love',
  [Symbol.iterator]: function * () {
    for (let key in this) {
      yield [key, this[key]] // yield [key, value] pair
    }
  }
}

尽管在大多数情况下,使用普通的for...in循环迭代对象会更好。

或者,您可以使用Object.keysObject.valuesObject.entries(ES7)将对象转换为可迭代数组。


1
你真的应该只需使用 [Symbol.iterator]() { return Object.entries(this) }(除了ES6中没有 Object.entries 这一事实之外)。 - Bergi
2
@Bergi 不行,这样不行。你必须返回一个迭代器,而不是一个可迭代对象。要重复使用 Object.entries 中的迭代器,你应该 return Object.entries(this)[Symbol.iterator]() - Leonid Beschastny
哎呀,我完全忘记了Object.entries是ES7的草案而不是ES6的一部分。 - Leonid Beschastny
2
@Bergi noop,Object.entries 返回一个由 [key, value] 对组成的普通 JS 数组。 - Leonid Beschastny
1
这是我实现的方式: for (let value of Object.values(this)) yield value - ThaJay
显示剩余2条评论

13

如果你不想使用生成器语法,你可以使用另一种方式来定义迭代器函数。

    var options = {
        male: 'John',
        female: 'Gina',
        rel: 'Love',
        [Symbol.iterator]: function () {
            var self = this;
            var values = Object.keys(this);
            var i = 0;
            return {
                next: function () {
                    return {
                        value: self[values[i++]],
                        done: i >= values.length
                    }
                }
            }
        }
    };

    for (var p of options) {
        console.log(`Property ${p}`);
    }

1
我喜欢这个答案,因为与生成器方法不同,这个选项允许你返回一个带有任何你想要的键的对象(而不仅仅是返回'value'和'done')。例如,你可以返回一个带有'key':values[i++]的对象。 - Stephen Agwu

6

在ES6中,普通对象(在这种情况下是options)不可迭代。您需要为您的对象定义一个迭代器或执行以下操作:

for(let k of Object.keys(options)) {
  console.log(`Property ${k}, ${options[k]}`);
};

1
ES6 中没有 Object.entries - Bergi
@Bergi,谢谢你!很高兴知道他们在ES7中重新引入了它。 - hallucinations
@hallucinations:它们也不会成为ES7的一部分。很可能是ES8。 - Felix Kling
@FelixKling 我看到了这个提案,我认为它们非常不错! - hallucinations

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接