Использование объектов в циклах For Of Loops


84

Почему нельзя использовать объекты в циклах for? Или это ошибка браузера? Этот код не работает в Chrome 42, говоря, что undefined не является функцией:

test = { first: "one"}

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

Тестировать массив или объект?
Кик Бутовски

9
@KickButtowski, разве ты не видишь? Это определенно объект.
Грин

4
for (let key of Object.keys (test)) {...}
clocksmith

Ответы:


69

Цикл for..of поддерживает только итерируемые объекты, такие как массивы, но не объекты.

Чтобы перебрать значения объекта, используйте:

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

3
@DanielHerr Наличие .iterableфункции-члена, из-за которой возникает ошибка, когда вы пытаетесь использовать ее для объекта (у которого ее нет). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Overv

4
Я имею в виду, почему у объектов этого нет? В чем проблема с его добавлением изначально?
Daniel Herr

3
@DanielHerr У меня нет ответа на этот вопрос, вам придется спросить людей, которые разрабатывают язык.
Overv

6
@DanielHerr Если бы «базовый класс» объекта был итерируемым, то же самое было бы с любым «подклассом» функции / даты / и т. Д. Среди других сложностей. См. Esdiscuss.org/topic/es6-iteration-over-object-values#content-5 для более полного / точного обсуждения вашего вопроса.
natevw 07

5
С этим решением для..в, разве вам технически не нужно делать проверку if (test.hasOwnProperty(key)){ ... }? Или это не нужно?
Tennisgent

40

Вы можете использовать этот синтаксис:

let myObject = {first: "one"};

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

Тем не мение, Object.entries имеет плохую поддержку прямо сейчас не работает в IE или iOS Safari. Ты будешьвероятно может понадобиться полифилл.


33

Если вы храните данные в хранилище "ключ-значение", используйте то,Map что специально предназначено для этой цели.

Если вам нужно использовать объект, ES2017 (ES8) позволяет вам использовать Object.values:

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

Если это еще не поддерживается, используйте polyfill: Альтернативная версия дляObject.values()

И, наконец, если вы поддерживаете более старую среду, которая не поддерживает этот синтаксис, вам придется прибегнуть к использованию forEachи Object.keys:

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

нельзя ли расширить прототип объекта для поддержки этого?
Sonic Soul,

1
@SonicSoul: технически да, но обычно не рекомендуется расширять прототип объекта, поскольку (почти) все наследуется от него.
Qantas 94 Heavy

1
Object.entriesможно полифиллировать, не касаясь прототипа.
mpen

5
Зачем использовать карты вместо объектов?
Даниэль Херр,

1
Есть ли преимущества в использовании этих сложных примеров перед простыми for-in?
1252748

18

Итератор, Iterable и цикл for..of в ECMAScript 2015 / ES6

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 , что означает, что он должен иметь объект со следующим методом. Следующий метод не принимает аргументов и должен возвращать объект с этими двумя свойствами.

done : сигнализирует, что последовательность завершена, когда true, а false означает, что может быть больше значений value : это текущий элемент в последовательности

Итак, чтобы сделать объект Iterable, то есть заставить его работать с for..of, мы можем:

1. Сделайте объект Iterable , назначив ему мистическое свойство @@ iterator через свойство Symbol.iterator. Вот как:

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.Use 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

Надеюсь это поможет!!!!!!


16

Я сделал объекты повторяемыми с помощью этого кода:

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({})) { }

48
Понятия не имею, почему это приемлемое решение. Модификация прототипа, кроме полифилла, всегда ужасная идея.
user1703761

2
@ user1703761 Это приемлемое решение, потому что оно работает. Объясните, пожалуйста, какие проблемы это вызовет, если это так ужасно.
Daniel Herr

9
Есть разные проблемы, в основном, с прямой совместимостью. Одним из примеров является то , что Array.prototype.includes который был ранее название содержит , но Moo Tools расширили прототип и реализация несовместима см bugzilla.mozilla.org/show_bug.cgi?id=1075059 также посмотреть библиотеку Desaster прототипа;)
user1703761

4
Я считаю, что при этом не будет проблем с прямой совместимостью, потому что, если бы к объектам был добавлен итератор, он бы перезаписал его, а если бы итератор был добавлен к подтипу объекта, он использовал бы итератор подтипа.
Daniel Herr

4
Привет, ребята, модифицировать прототип - плохая идея !!! Позорите ОП за то, что он действительно дал ответ на вопрос!
NiCk Newman

12

Поскольку литерал объекта не имеет свойства Symbol.iterator . Чтобы быть конкретным, вы можете перебирать String , Array , Map , Set , arguments , NodeList (широко не поддерживается) и Generator с помощью цикла for ... of .

Чтобы справиться с итерацией Object Literal, у вас есть два варианта.

для ... в

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

Object.keys + forEach

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

3

Ответ - нет. Использовать For..Of с литералами Object невозможно.

Я согласен с Overv, что For..Of предназначен только для итераций. У меня был точно такой же вопрос, потому что я использую объекты для перебора ключей и значений с помощью for..in. Но я только что понял, что для этого нужны ES6 MAPS и SETS .

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 ().

Кроме того, ваши ключи не ограничиваются строками. Вы можете использовать числа, объекты или другие литералы.


2

У объектных литералов нет встроенных итераторов, необходимых для работы с 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

Использовать ключи каждый раз сложнее, чем один раз добавить итератор. Кроме того, Object.keys () - это ES5.
Daniel Herr

1

Можно определить итератор для любого передающего объекта, таким образом вы можете поместить различную логику для каждого объекта.

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])
}

1

Я просто сделал следующее, чтобы легко утешить свои вещи.

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


0

Как насчет использования

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);
}

0

в 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}

Вот файл jsfiddle. В выходе вы получите объект с "value"и "done"ключами. "Value"содержит все, что вы хотите , и "done"текущее состояние итерации в bool.


0

Используя 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"
});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.