Как объект Javascript может стать итерируемым с помощью оператора forof?

Я хотел бы установить свойство 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}`);
 }

person cicciosgamino    schedule 05.03.2016    source источник
comment
Чтобы использовать цикл for...of, коллекция должна иметь свойство [Symbol.iterator]. Литералы объектов не имеют такого свойства, только массивы, наборы, карты и т. д.   -  person adeneo    schedule 06.03.2016
comment
Вы хотите использовать Map для этого.   -  person Bergi    schedule 06.03.2016


Ответы (3)


Чтобы использовать цикл 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.keys, Object.values или Object.entries (ES7).

person Leonid Beschastny    schedule 05.03.2016
comment
Вы действительно должны просто сделать [Symbol.iterator]() { return Object.entries(this) } (кроме того факта, что в ES6 нет Object.entries) - person Bergi; 06.03.2016
comment
@ Берги, нет, это не сработает. Вы должны вернуть итератор, а не итерируемый объект. Чтобы повторно использовать итератор из Object.entries, вы должны return Object.entries(this)[Symbol.iterator](). - person Leonid Beschastny; 06.03.2016
comment
Ой, я совсем забыл, что Object.entries — это черновик ES7, а не часть ES6. - person Leonid Beschastny; 06.03.2016
comment
Разве entries уже не возвращает итератор? Но, возможно, я ошибаюсь, или есть несколько черновиков. - person Bergi; 06.03.2016
comment
@Bergi noop, Object.entries возвращает простой массив JS из [key, value] пар. - person Leonid Beschastny; 06.03.2016
comment
Он должен возвращать только this[key]. из спецификации: for (variable of iterable) {statement} developer.mozilla. org/en-US/docs/Web/JavaScript/Reference/ - person ThaJay; 05.04.2018
comment
Вот как я это реализовал: for (let value of Object.values(this)) yield value - person ThaJay; 06.04.2018

Если вы не хотите использовать синтаксис генератора, вы можете использовать другой способ определения функции итератора.

    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}`);
    }
person Dmitriy    schedule 05.03.2016
comment
Мне нравится этот ответ, потому что, в отличие от метода генератора, эта опция позволяет вам вернуть объект с любыми ключами, которые вы хотите (в отличие от простого возврата «значение» и «готово»). Например, вы можете вернуть объект с «ключом»: значения[i++]. - person Stephen Agwu; 17.01.2018

Обычные объекты (в данном случае options) не поддерживают итерацию в ES6. Вам нужно либо определить итератор для вашего объекта, либо сделать что-то вроде:

for(let k of Object.keys(options)) {
  console.log(`Property ${k}, ${options[k]}`);
};
person hallucinations    schedule 05.03.2016
comment
В ES6 нет Object.entries - person Bergi; 06.03.2016
comment
@Берги, спасибо за это! Рад узнать, что они возвращают его в ES7. - person hallucinations; 06.03.2016
comment
@hallucinations: Они также не будут частью ES7. Скорее всего ES8. - person Felix Kling; 06.03.2016
comment
@FelixKling Я видел это предложение и подумал, что это так! - person hallucinations; 06.03.2016