Ітерація через властивості об'єкта


2038

var obj = {
    name: "Simon",
    age: "20",
    clothing: {
        style: "simple",
        hipster: false
    }
}

for(var propt in obj){
    console.log(propt + ': ' + obj[propt]);
}

Як змінна proptпредставляє властивості об'єкта? Це не вбудований метод чи властивість. Чому він придумує кожну властивість об’єкта?


11
if (typeof(obj[propt]) === 'object') {/ * Зробіть це знову * /}
noob

13
Ну, справді вибачте за це питання. Я знаю, що таке цикл, я не міг обернутися головою "прокручування властивостей об'єкта", яка, на мою думку, зараз очищена. Крім того, вони порекомендували мені "JavaScript крок за кроком 2-е видання - Стів Сюрінг в школі.
Рафай

242
Це прекрасне питання для початківців. Додам, що я маю 15-річний досвід роботи з іншими мовами, і мені потрібна була відповідь. Я б плюс 2000, якби міг.
Nathan C. Tresch

60
Божевільно, але я приходжу на цю сторінку кожні кілька місяців, щоб вивчити синтаксис, як це зробити. Я не намагаюся згадувати, як це зробити ... Я просто пам’ятаю, що ця сторінка завжди тут, так
HDave

14
Це найдивніша сторінка, яку я бачив у StackOverflow. Якщо ви уважно читаєте запитання, лише одна відповідь починає намагатися відповісти на те, що насправді задають, і вона має бал -6. Відповідь найвищої оцінки, яка була прийнята, не тільки не відповідає, а просто помиляється.

Відповіді:


2425

Ітерація над властивостями вимагає цієї додаткової hasOwnPropertyперевірки:

for (var prop in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, prop)) {
        // do stuff
    }
}

Це необхідно, тому що прототип об'єкта містить додаткові властивості для об'єкта, які технічно є частиною об'єкта. Ці додаткові властивості успадковуються від базового класу об'єктів, але все ще є властивостями obj.

hasOwnProperty просто перевіряє, чи це властивість, характерна для цього класу, а не одна, успадкована від базового класу.


Можна також зателефонувати hasOwnPropertyчерез сам об’єкт:

if (obj.hasOwnProperty(prop)) {
    // do stuff
}

Але це не вдасться, якщо в об’єкта є незв'язане поле з такою ж назвою:

var obj = { foo: 42, hasOwnProperty: 'lol' };
obj.hasOwnProperty('foo');  // TypeError: hasOwnProperty is not a function

Ось чому безпечніше зателефонувати через це Object.prototype:

var obj = { foo: 42, hasOwnProperty: 'lol' };
Object.prototype.hasOwnProperty.call(obj, 'foo');  // true

21
@BT Відповідно до документації Mozilla : "Якщо ви хочете розглянути лише властивості, приєднані до самого об'єкта, а не його прототипи, використовуйте getOwnPropertyNames або виконайте перевірку hasOwnProperty (також можна використовувати перевірку властивостіIsEnumerable)."
davidmdem

3
Який саме сенс дзвонити object.hasOwnProperty()? Чи не означає той факт, який propertyмає якусь цінність, означає, що це в object?
Alex S

6
Тому що, Алекс S, прототип об'єкта містить додаткові властивості для об'єкта, які технічно є частиною об'єкта. Вони успадковуються від базового класу об'єктів, але вони все ще є властивостями. hasOwnProperty просто перевіряє, чи це властивість, характерна для цього класу, а не одна, успадкована від базового класу. Гарне пояснення: brianflove.com/2013/09/05/javascripts-hasownproperty-method
Кайл Ріхтер

87
Я вважаю, що варто зазначити, що Object.keys (obj) тепер набагато краще рішення для отримання ключів самого об'єкта. Посилання на документацію Mozilla: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Кайл Ріхтер

9
Немає важливої ​​інформації. propertyтут є рядок, його слід було називати propertyName. В іншому випадку можна викликати плутанину у новичок JS, як я, тобто що робити всередині if.
Неоліск

1135

Станом на JavaScript 1.8.5 ви можете використовувати Object.keys(obj)для отримання масиву властивостей, визначених на самому об'єкті (тих, для яких повертається true obj.hasOwnProperty(key)).

Object.keys(obj).forEach(function(key,index) {
    // key: the name of the object key
    // index: the ordinal position of the key within the object 
});

Це краще (і читабельніше), ніж використання циклу для введення.

Він підтримується в таких браузерах:

  • Firefox (Gecko): 4 (2,0)
  • Хром: 5
  • Internet Explorer: 9

Див в Mozilla Developer Network Object.keys () «сек посилання Для докладної інформації.


7
Зараз це більш широко підтримується: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Dom Vinyard

3
І якщо вам потрібна підтримка старих браузерів, ви можете скористатися цим поліфілом
KyleMit

27
У середовищах, які підтримують цю мовну конструкцію, цей метод дозволяє викликати Array.foreach: Object.keys(myObject).forEach(function(key,index) { //key = the name of the object key //index = the ordinal position of the key within the object });
Todd Price

4
@ AJ_83 Немає хорошого способу вирватися з forEach (). Використовуйте деякий () в цьому випадку, і поверніться true, щоб зламати
Daniel Z.

11
Чому це читабельніше, ніж для входу? for candidate in candidateStatus... здається читабельним для мене
Jona

308

Дівчата та хлопці, у нас в 2019 році і у нас не так багато часу для набору тексту ... Тож давайте зробимо цей класний новий фантазійний ECMAScript 2016:

Object.keys(obj).forEach(e => console.log(`key=${e}  value=${obj[e]}`));

17
Чим це відрізняється від відповіді Денні Р?
крильгар

27
Це онлінер і використовує карту замість ForEach. А також console.log satement, можливо, цікавий для деяких людей.
Френк Рот

Приємно, що це не працює, коли obj=window.performance.memory: - / Куди як for in. тобтоvar obj = window.performance.memory; for( key in obj ) console.log( 'key=' + key + ' val=' + obj[key] );
Michaelangel007

2
window.performance.memoryпідтримується лише chrome і Object.keys(obj)повертає порожній масив. Це не має нічого спільного .map.
Френк Рот

У випадку, якщо хтось не захоче мавпоподібно реструктуризувати цей однолінійний параметр, щоб робити більше, ніж одно e, я опублікував цю суть. Це в основному так само, як і більшість хеш-реалізацій, і використовує ( (key) => (value) )замість них { key => value }, але якщо вам раніше не доводилося стикатися з цим, це може допомогти вам візуалізувати його краще: gist.github.com/the-nose-knows/9f06e745a56ff20519707433e28a4fa8
kayleeFrye_onDeck

216

Це for...in statement( MDN , специфікація ECMAScript ).

Ви можете прочитати його як " для кожного властивості Вobj об'єкт, привласнити кожне властивість до PROPT змінному , в своєї черги».


1
Велике спасибі, я зараз це розумію. Я стукав головою, перебираючи книги та Google.
Рафай

21
Згоден з @RightSaidFred, з inоператором і forзаяви не беруть участі в загальному, for-inзаява являє собою граматичне по собі: for ( LeftHandSideExpression in Expression ),for ( var VariableDeclarationNoIn in Expression )
CMS

2
Як не дивно, ця відповідь має стільки голосів, тим більше, що ці популярні коментарі, здається, суперечать їй.
Doug Molineux

9
Чому це позначено як відповідь? Це, можливо, найменш корисний у цій темі ..
computrius

3
Найменш корисна відповідь? Залежить від того, що, на вашу думку, запитував ОП коли я вперше прочитав питання, здавалося, що це збентежено здивування щодо механізму, за допомогою якого змінна може бути використана для перевірки властивостей об'єкта, і що ця відповідь красномовно пояснює (незважаючи на помилку "за" в "). Питання «Чому він придумав кожне властивість» я бачу можу означати , що ОП шукали hasOwnProperty , але не знає про це, але я думаю , що більш імовірно , це було те , що ОП хоче знати, і неправильно прийнятий правильним відповідь на інше запитання. :-)
Bumpy

157

У сучасних реалізаціях ES ви можете використовувати Object.entries:

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

або

Object.entries(obj).forEach(([key, value]) => ...)

Якщо ви просто хочете повторити значення, використовуйте Object.values:

for (const value of Object.values(obj)) { }

або

Object.values(obj).forEach(value => ...)

це було б найкращим рішенням (object.entries ...), але я не можу його використовувати. Коли ви хочете зробити це кілька разів і не можете підтримати його у своїх рамках, ви можете використовувати поліфайл на цій сторінці: developer.mozilla.org/nl/docs/Web/JavaScript/Reference/…
Маріо

Третя пропозиція чудова, якщо ви маєте лише значення властивостей. Дивовижно!
Гінзбург


27

Якщо ваше середовище підтримує ES2017, я рекомендую Object.entries :

Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key} ${value}`);
});

Як показано в документації Mozillas Object.entries () :

Метод Object.entries () повертає масив власних перелічених властивостей [ключ, значення] парного об'єкта в тому ж порядку, що і параметр for ... in цикл (різниця полягає в тому, що цикл for-in перераховується властивості і в ланцюзі прототипу).

В основному за допомогою Object.entries ми можемо відмовитись від наступного додаткового кроку, необхідного для старшого для ... у циклі:

// This step is not necessary with Object.entries
if (object.hasOwnProperty(property)) {
  // do stuff
}

22

jquery дозволяє вам це зробити зараз:

$.each( obj, function( key, value ) {
  alert( key + ": " + value );
});

1
$.each({foo:1, length:0, bar:2}, function(k,v){console.log(k,v)})$ .each не підходить для об'єктів. Якщо у об'єкта є властивість довжини, а його значення дорівнює нулю, весь об'єкт трактується так, ніби він був порожнім масивом.
Боб Штейн

Детальніше, чому я вважаю, що це підхід, що викликає помилок .
Боб Штейн


18

Наведені вище відповіді трохи дратують, тому що вони не пояснюють, що ви робите всередині циклу for, після того, як переконаєтесь, що це об'єкт: ВИ НЕ ДОПУСКАТИ ЇЇ Прямо! Ви фактично доставили лише КЛЮЧ, який потрібно застосувати до OBJ:

var obj = {
  a: "foo",
  b: "bar",
  c: "foobar"
};

// We need to iterate the string keys (not the objects)
for(var someKey in obj)
{
  // We check if this key exists in the obj
  if (obj.hasOwnProperty(someKey))
  {
    // someKey is only the KEY (string)! Use it to get the obj:
    var myActualPropFromObj = obj[someKey]; // Since dynamic, use [] since the key isn't literally named "someKey"

    // NOW you can treat it like an obj
    var shouldBeBar = myActualPropFromObj.b;
  }
}

Це все безпечно ECMA5. Навіть працює в кульгавій версії JS, як Rhino;)


15

Щоб додати використання ES2015, Reflect.ownKeys(obj)а також ітерацію над властивостями за допомогою ітератора.

Наприклад:

let obj = { a: 'Carrot', b: 'Potato', Car: { doors: 4 } };

можна повторити

// logs each key
Reflect.ownKeys(obj).forEach(key => console.log(key));

Якщо ви хочете повторити значення значень клавіш об'єкта, ви можете визначити iterator , як ітератори за замовчуванням ітераторів JavaScipts для рядків, масивів, набраних масивів, Map і Set.

JS спробує ітерацію за допомогою властивості ітератора за замовчуванням, яке необхідно визначити як Symbol.iterator .

Якщо ви хочете мати можливість переглядати всі об'єкти, ви можете додати його як прототип об'єкта:

Object.prototype[Symbol.iterator] = function*() { 
    for(p of Reflect.ownKeys(this)){ yield this[p]; }
}

Це дозволить вам переглядати значення об'єкта з циклом for ..., наприклад:

for(val of obj) { console.log('Value is:' + val ) }

Увага : На момент написання цієї відповіді (червень 2018 року) всі інші браузери, окрім IE, підтримують генератори та for...ofітерацію черезSymbol.iterator


Хоча ви насправді не відповідаєте на питання ОП, це було дуже корисно для мене, я ще не знав про Reflect.
Міхель

15
if(Object.keys(obj).length) {
    Object.keys(obj).forEach(key => {
        console.log("\n" + key + ": " + obj[key]);
    });
}

// *** Explanation line by line ***

// Explaining the bellow line
// It checks if obj has at least one property. Here is how:
// Object.keys(obj) will return an array with all keys in obj
// If there is no keys in obj, it will return empty array = []
// Then it will get it's length, if it has at least one element,
// it's bigger than 0 which evaluates to true and the bellow 
// code will be executed.
// Else means it's length = 0 which evaluates to false
// NOTE: you can use Object.hasOwnProperty() instead of Object.keys(obj).length
if(Object.keys(obj).length) {

    // Explaining the bellow line
    // Just like in the previous line, this returns an array with
    // all keys in obj (because if code execution got here, it means 
    // obj has keys.) 
    // Then just invoke built-in javascript forEach() to loop
    // over each key in returned array and calls a call back function 
    // on each array element (key), using ES6 arrow function (=>)
    // Or you can just use a normal function ((key) { blah blah }).
    Object.keys(obj).forEach(key => {

        // The bellow line prints out all keys with their 
        // respective value in obj.
        // key comes from the returned array in Object.keys(obj)
        // obj[key] returns the value of key in obj
        console.log("\n" + key + ": " + obj[key]);
    });
}

3
Привіт, ви можете додати більше інформації про свою відповідь, якщо тільки код не допомагає.
Ніколя

Привіт @Nicolas Я додав до коду пояснення по рядку. Повідомте мене, якщо це все ще не зрозуміло
Фуад Букредін

Оскільки forEach пропускає порожні значення , я думаю, ви могли б позбутися від того, що і просто так, Object.keys(obj).forEach(e => console.log(`key=${e} value=${obj[e]}`));як відповідь Френка Рота.
Darkproduct

12

Цикл for ... in представляє кожну властивість об'єкта, тому що це як цикл for. Ви визначаєте підсказку в циклі for ... in, виконуючи:

    for(var propt in obj){
alert(propt + ': ' + obj[propt]);
}

A для ... в циклі ітерається через перелічені властивості об'єкта. Незалежно від зміни, яку ви визначаєте, або ставите в цикл for ... in, змінюється щоразу, коли вона переходить до наступної властивості, яку вона ітератує. Змінна в циклі for ... in циклічно повторюється через клавіші, але її значення є значенням ключа. Наприклад:

    for(var propt in obj) {
      console.log(propt);//logs name
      console.log(obj[propt]);//logs "Simon"
    }

Ви можете бачити, як змінна відрізняється від значення змінної. Навпаки, цикл для ... циклу робить навпаки.

Я сподіваюся, що це допомагає.


11
let obj = {"a": 3, "b": 2, "6": "a"}

Object.keys(obj).map((item) => {console.log("item", obj[item])})

// a
// 3
// 2

1
Як було сказано в інших коментарях, forEachтут більше підходить, оскільки mapпризначений повернути новий масив з результатами виклику блоку коду на кожній ітерації. Але нас цікавлять лише побічні наслідки кожної ітерації, а не повернене значення, отже, нам не потрібен новий масив, який mapнам дає.
Денні


10

Можна використовувати Лодаш. Документація

var obj = {a: 1, b: 2, c: 3};
_.keys(obj).forEach(function (key) {
    ...
});

10
Чому на Землі ця "відповідь" має 10 оновлених результатів? Він повністю не відповідає на питання. Я починаю втрачати віру в інтелект середнього розробника JS.
developerbmw

1
@developerbmw Я розумію, що використовувати функції ES6 - це більш правильний спосіб, але я відповів рік тому. Будь ласка, поділіться своїми думками з нами, коли у вас є хвилина.
viktarpunko

1
Ідея полягає в тому, щоб більше зосередитися на рідних методах, а не пропонувати користувачеві додати на свою сторінку бібліотеку 10000 рядків. Не зрозумійте мене неправильно, я люблю використовувати Лодаш, але є час і місце, і це не це.

9

Ваш forцикл повторюється над усіма властивостями об'єкта obj. proptвизначено в першому рядку для циклу. Це рядок, який є назвою властивості objоб'єкта. У першій ітерації циклу proptбуло б "ім'я".


9

Об'єкти в JavaScript - це колекція властивостей, і тому їх можна зафіксувати в а для кожного оператора.

Ви повинні розглядати це objяк основну колекцію цінностей.


! з важливою відмінністю, що ці "списки властивостей" можуть мати імена як ключі, тоді як звичайні масиви JS можуть мати лише цифри як ключі.
Qqwy

9

Сьогодні ти можеш перетворити стандартний об'єкт JS в ітерабельний об’єкт, просто додавши метод Symbol.iterator. Тоді ви можете використовувати for ofцикл і отримувати доступ до його значень безпосередньо або навіть можете використовувати оператор розповсюдження і на об’єкті. Класно. Подивимося, як ми можемо це зробити:

var o = {a:1,b:2,c:3},
    a = [];
o[Symbol.iterator] = function*(){
                       var ok = Object.keys(this);
                            i = 0;
                       while (i < ok.length) yield this[ok[i++]];
                     };
for (var value of o) console.log(value);
// or you can even do like
a = [...o];
console.log(a);


1
Цікавий спосіб це зробити. Дякую за function*відкриття!
Бендж

5

Якщо ви працюєте з Node, я рекомендую:

Object.keys(obj).forEach((key, index) => {
    console.log(key);
});

5

Хоча відповідь з найвищим рейтингом є правильною, ось варіант альтернативного використання, тобто якщо ви перебираєте об'єкт і хочете створити масив в кінці. Використовуйте .mapзамістьforEach

const newObj = Object.keys(obj).map(el => {
    //ell will hold keys 
   // Getting the value of the keys should be as simple as obj[el]
})

4

Також додавання рекурсивного способу:

function iterate(obj) {
    // watch for objects we've already iterated so we won't end in endless cycle
    // for cases like var foo = {}; foo.bar = foo; iterate(foo);
    var walked = [];
    var stack = [{obj: obj, stack: ''}];
    while(stack.length > 0)
    {
        var item = stack.pop();
        var obj = item.obj;
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                  // check if we haven't iterated through the reference yet
                  var alreadyFound = false;
                  for(var i = 0; i < walked.length; i++)
                  {
                    if (walked[i] === obj[property])
                    {
                      alreadyFound = true;
                      break;
                    }
                  }
                  // new object reference
                  if (!alreadyFound)
                  {
                    walked.push(obj[property]);
                    stack.push({obj: obj[property], stack: item.stack + '.' + property});
                  }
                }
                else
                {
                    console.log(item.stack + '.' + property + "=" + obj[property]);
                }
            }
        }
    }
}

Використання:

iterate({ foo: "foo", bar: { foo: "foo"} }); 

1
@faiz - дивіться мої коментарі, це захист від того, щоб застрягти в нескінченному циклі, коли ви постійно проходите об'єкт, який має циклічні посилання
Ondrej Svejdar

3

Що для..in циклу, це те, що він створює нову змінну (var someVariable), а потім зберігає кожну властивість даного об'єкта в цій новій змінній (someVariable) по черзі. Тому, якщо ви використовуєте блок {}, ви можете зробити ітерацію. Розглянемо наступний приклад.

var obj = {
     name:'raman',
     hobby:'coding',
     planet:'earth'
     };

for(var someVariable in obj) {
  //do nothing..
}

console.log(someVariable); // outputs planet

Оновлення цього, враховуючи його простоту. У моєму випадку використання мені потрібно перевірити всі атрибути в об’єкті на наявність хитких значень - NaNs, nulls, undefined (вони були точками на графіку, і ці значення не дозволяли графіку малювати). Щоб отримати значення замість імені, у циклі ви просто зробите obj[someVariable]. Можливо, причина, на яку вона була стільки заперечена, полягає в тому, що вона не є рекурсивною. Тож це не буде адекватним рішенням, якщо у вас є високоструктурований об’єкт.
Кетрін Осборн

@KatharineOsborne або, можливо, це тому, що наступна фраза є трохи виразною: "Тому, якщо ви використовуєте блок {}, ви можете повторити ітерацію". Код говорить більше, ніж текст.
bvdb

3

Тут я повторюю кожен вузол і створюю значущі імена вузлів. Якщо ви помічаєте, instanceOf Array та instanceOf Object в значній мірі робить те саме (у моєму додатку я надаю іншу логіку)

function iterate(obj,parent_node) {
    parent_node = parent_node || '';
    for (var property in obj) {
        if (obj.hasOwnProperty(property)) {
            var node = parent_node + "/" + property;
            if(obj[property] instanceof Array) {
                //console.log('array: ' + node + ":" + obj[property]);
                iterate(obj[property],node)
            } else if(obj[property] instanceof Object){
                //console.log('Object: ' + node + ":" + obj[property]);
                iterate(obj[property],node)
            }
            else {
                console.log(node + ":" + obj[property]);
            }
        }
    }
}

зауважте - мене надихає відповідь Ондрей Швейдар. Але це рішення має кращі показники та менш неоднозначні


3

Ви в основному хочете переглядати кожну властивість об'єкта.

JSFiddle

var Dictionary = {
  If: {
    you: {
      can: '',
      make: ''
    },
    sense: ''
  },
  of: {
    the: {
      sentence: {
        it: '',
        worked: ''
      }
    }
  }
};

function Iterate(obj) {
  for (prop in obj) {
    if (obj.hasOwnProperty(prop) && isNaN(prop)) {
      console.log(prop + ': ' + obj[prop]);
      Iterate(obj[prop]);
    }
  }
}
Iterate(Dictionary);

obj(prop)<- TypeError: obj не є функцією
le_m

@le_m мій поганий. Я повинен випадково вийняти hasOwnPropertyатрибут. Це має працювати зараз.
HovyTech

2

Я хочу додати відповіді вище, тому що у вас можуть бути різні наміри від Javascript. Об'єкт JSON та об'єкт Javascript - це різні речі, і ви, можливо, захочете повторити властивості об’єкта JSON, використовуючи рішення, запропоновані вище, а потім здивуватися.

Припустимо, у вас є об'єкт JSON, наприклад:

var example = {
    "prop1": "value1",
    "prop2": [ "value2_0", value2_1"],
    "prop3": {
         "prop3_1": "value3_1"
    }
}

Неправильний спосіб повторити його "властивості":

function recursivelyIterateProperties(jsonObject) {
    for (var prop in Object.keys(example)) {
        console.log(prop);
        recursivelyIterateProperties(jsonObject[prop]);
    }
}

Ви можете бути здивовані бачити протоколювання консолі 0, 1і т.д. , коли Перебір властивостей prop1і prop2іprop3_1 . Ці об'єкти - це послідовності, а індекси послідовності є властивостями цього об’єкта в Javascript.

Кращим способом рекурсивної ітерації властивостей об'єкта JSON буде спочатку перевірити, чи є цей об'єкт послідовністю чи ні:

function recursivelyIterateProperties(jsonObject) {
    for (var prop in Object.keys(example)) {
        console.log(prop);
        if (!(typeof(jsonObject[prop]) === 'string')
            && !(jsonObject[prop] instanceof Array)) {
                recursivelyIterateProperties(jsonObject[prop]);

            }

     }
}

1

Для подальшого уточнення прийнятої відповіді варто зауважити, що якщо інстанціювати об’єкт із символом a, var object = Object.create(null)то object.hasOwnProperty(property)викличе TypeError. Тож, щоб бути в безпечній стороні, вам потрібно назвати це з прототипу так:

for (var property in object) {
    if (Object.prototype.hasOwnProperty.call(object, property)) {
        // do stuff
    }
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.