Використання об’єктів у For Of Loops


83

Чому не можна використовувати об'єкти у циклах for for? Або це помилка браузера? Цей код не працює в Chrome 42, кажучи, що undefined не є функцією:

test = { first: "one"}

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

Тест - це масив чи об'єкт?
Kick Buttowski

9
@KickButtowski, ти не бачиш? Це точно об’єкт.
Зелений

4
for (let key of Object.keys (test)) {...}
годинник

Відповіді:


69

Цикл for..of підтримує лише такі об'єкти, як і масиви, а не об'єкти.

Для перебору значень об’єкта використовуйте:

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

3
@DanielHerr Наявність .iterableфункції-члена, звідки і виникає помилка при спробі використовувати її на об’єкті (який її не має). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Оверв

4
Я маю на увазі, чому у об'єктів цього немає? У чому була б проблема з рідним додаванням?
Даніель Герр,

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

6
@DanielHerr Якби "базовий клас" Об'єкта був ітерабельним, як і будь-який "підклас" функції / дати / тощо серед інших ускладнень. Дивіться esdiscuss.org/topic/es6-iteration-over-object-values#content-5 для більш детального / точного обговорення вашого питання.
natevw

5
З цим рішенням for..in, вам все-таки технічно не доводиться перевіряти наявність if (test.hasOwnProperty(key)){ ... }? Або це не потрібно?
тенісгент

39

Ви можете використовувати цей синтаксис:

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

не вдалося розширити прототип Object, щоб підтримати це?
Sonic Soul

1
@SonicSoul: технічно так, але зазвичай не рекомендується розширювати прототип Object, оскільки (в основному) все успадковується від нього.
Qantas 94 Важкий

1
Object.entriesможе шляхом багаторазового заповнення, не торкаючись прототипу.
mpen

5
Навіщо використовувати замість об’єктів карти?
Даніель Герр,

1
Чи є якась перевага у використанні цих складних прикладів перед простими for-in?
1252748

18

Ітератор, ітерабельний та 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 , тобто він повинен мати об'єкт із наступним методом. Наступний метод не бере аргументів, і він повинен повернути об'єкт із цими двома властивостями.

зроблено : сигнали , що послідовність закінчилася , коли істинно, і неправдиві кошти можуть бути більше значення значення : це поточний елемент в послідовності

Отже, зробити об’єкт ітерабельним , тобто змусити його працювати з for..of, ми можемо:

1. Зробіть об’єкт ітерувальним , призначивши йому властивість mystical @@ 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. Використовуйте 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.Use 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({})) { }

47
Поняття не маю, чому це прийняте рішення. Модифікація прототипу, за винятком випадків, коли це поліфіл, - це завжди жахлива ідея.
user1703761

2
@ user1703761 Це прийняте рішення, оскільки воно працює. Поясніть, будь ласка, які проблеми це може спричинити, якщо це так жахливо.
Даніель Герр

9
Є всілякі проблеми, головним чином, проблеми сумісності. Одним із прикладів є те, що Array.prototype.includes, який раніше містив імена, але Moo Tools розширив прототип, і реалізація була несумісною, див. Bugzilla.mozilla.org/show_bug.cgi?id=1075059 Також знайдіть прототип бібліотеки desaster;)
user1703761

4
Я вважаю, що це не матиме проблем із сумісністю вперед, тому що якби ітератор був доданий до об'єктів, це перезаписало б його, а якщо ітератор був доданий до підтипу об'єкта, він би використовував ітератор підтипу.
Даніель Герр,

4
Гей, хлопці, модифікувати прототип - погана ідея !!! Давайте ганьбити ОП за те, що насправді дав відповідь на запитання!
NiCk Newman

12

Оскільки літерал об’єкта не має властивості Symbol.iterator . Якщо бути конкретним, ви можете перебирати лише рядки , масиви , карти , набори , аргументи , NodeList (не широко підтримується) та генератор за допомогою циклу for ... of .

Щоб мати справу з ітерацією об’єктного літералу, у вас є два варіанти.

для ... в

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 призначений лише для ітерацій. У мене було точно таке саме запитання, оскільки я використовую Objects для перебору ключів і значень за допомогою for..in. Але я щойно зрозумів, що для цього призначені КАРТИ та НАБОРИ ES6 .

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.
Даніель Герр

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.