Як провести цикл через звичайний JavaScript-об’єкт з об'єктами як членами?


1599

Як я можу переглядати всі члени об’єкта JavaScript, включаючи значення, які є об'єктами.

Наприклад, як я міг переглядати це (доступ до "свого_імені" та "вашої_повідомлення" для кожного)?

var validation_messages = {
    "key_1": {
        "your_name": "jimmy",
        "your_msg": "hello world"
    },
    "key_2": {
        "your_name": "billy",
        "your_msg": "foo equals bar"
    }
}

11
можливий дублікат Loop через JavaScript-об’єкт
BuZZ-dEE

Відповіді:


2113
for (var key in validation_messages) {
    // skip loop if the property is from prototype
    if (!validation_messages.hasOwnProperty(key)) continue;

    var obj = validation_messages[key];
    for (var prop in obj) {
        // skip loop if the property is from prototype
        if (!obj.hasOwnProperty(prop)) continue;

        // your code
        alert(prop + " = " + obj[prop]);
    }
}

13
Internet Explorer не погоджується ( зітхаючи ), каже, що "Object не підтримує цю властивість чи метод", коли ви робите obj [prop]. Я ще не повинен знайти рішення для цього.
user999717

2
@MildFuzz насправді має сенс, якщо ви вважаєте, що об'єкти JS не потрібні, мають цифрові клавіші. Ви не можете просто повторити об'єкт. JS's for inдуже схожий на традиційний foreach.
Джейк Вілсон

4
для ... in - це гарне рішення, але якщо ви використовуєте обіцянки в циклі for () - будьте обережні, тому що, якщо ви створите var у циклі, ви не можете використовувати його у функції функцій для обіцянки. Ви змінюєте цикл лише один раз, тому він має у кожній тодішній функції те саме, навіть останнє значення. Якщо у вас є ця проблема, спробуйте "Object.keys (obj) .forEach" або мою відповідь нижче.
Бібер

775

Відповідно до ECMAScript 5, ви можете комбінувати Object.keys()та Array.prototype.forEach():

var obj = {
  first: "John",
  last: "Doe"
};

//
//	Visit non-inherited enumerable keys
//
Object.keys(obj).forEach(function(key) {

  console.log(key, obj[key]);

});


34
+1 для стислості коду, але, мабуть, не працює настільки ефективно, як напрочуд. JSPerf - for in vs Object.keys
techiev2

6
Остерігайтеся цієї помилки, використовуючи такий підхід: "TypeError: Object.keys викликається не-об'єктом". for ... in ... hasOwnPropertyМодель можна назвати на що - небудь, наскільки я можу сказати (об'єкт, масив порожній, невизначений, істина, брехня, номер примітивні, об'єкти).
theazureshadow

2
Зауважте, що IE7 не підтримує це.
Пол Д. Уейт

3
@ techiev2 ці тести ніколи не були дійсними. Дивіться мої оновлені дані про поточний стан продуктивності: jsperf.com/objdir/20
OrganicPanda

4
@ techiev2: це не те, Object.keys()що робить це повільно, це швидше forEach()і повторний доступ до .length! Якщо ви використовуєте класичний for-loop замість нього, це майже вдвічі швидше, ніж for..in+ hasOwnProperty()у Firefox 33.
CodeManX

384

Проблема з цим

for (var key in validation_messages) {
   var obj = validation_messages[key];
   for (var prop in obj) {
      alert(prop + " = " + obj[prop]);
   }
}

полягає в тому, що ви також будете перебирати прототип примітивного об'єкта.

За допомогою цього ви уникнете цього:

for (var key in validation_messages) {
   if (validation_messages.hasOwnProperty(key)) {
      var obj = validation_messages[key];
      for (var prop in obj) {
         if (obj.hasOwnProperty(prop)) {
            alert(prop + " = " + obj[prop]);
         }
      }
   }
}

46
Коротше: перевірити hasOwnPropertyвсередині ваших for- inпетель.
Rory O'Kane

59
Зауважте, що це необхідно лише в тому випадку, якщо ваш об'єкт має методи прототипу. Наприклад, якщо об’єкт, через який ви перебираєтеся, є лише об'єктом JSON, ця перевірка вам не знадобиться.
gitaarik

6
@rednaw Для безпеки я використовую цю перевірку, оскільки Object.prototype можна змінювати. Жоден розумний скрипт не зробив би цього, але ви не можете контролювати, які сценарії можуть бути запущені на вашій сторінці за допомогою божевільних розширень браузера. Розширення веб-переглядача запускаються на вашій сторінці (у більшості браузерів), і вони можуть спричинити незвичайні проблеми (наприклад, встановити window.setTimeout на нуль!).
robocat


328

У ES6 / 2015 ви можете провести цикл через такий об’єкт: (за допомогою функції стрілки )

Object.keys(myObj).forEach(key => {
  console.log(key);        // the name of the current key.
  console.log(myObj[key]); // the value of the current key.
});

jsbin

У ES7 / 2016 ви можете використовувати Object.entriesзамість цього об’єкта Object.keysта переходити до нього:

Object.entries(myObj).forEach(([key, val]) => {
  console.log(key); // the name of the current key.
  console.log(val); // the value of the current key.
});

Вищезазначене також працює як однолінійний :

Object.entries(myObj).forEach(([key, val]) => console.log(key, val));

jsbin

У випадку, якщо ви також хочете проходити цикл через вкладені об'єкти, ви можете використовувати рекурсивну функцію (ES6):

const loopNestedObj = obj => {
  Object.keys(obj).forEach(key => {
    if (obj[key] && typeof obj[key] === "object") loopNestedObj(obj[key]); // recurse.
    else console.log(key, obj[key]); // or do something with key and val.
  });
};

jsbin

Те саме, що функція вище, але з ES7 Object.entries() замість Object.keys():

const loopNestedObj = obj => {
  Object.entries(obj).forEach(([key, val]) => {
    if (val && typeof val === "object") loopNestedObj(val); // recurse.
    else console.log(key, val); // or do something with key and val.
  });
};

Тут ми перебираємо через вкладені об'єкти змінити значення та повертаємо новий об’єкт за один раз, використовуючи Object.entries()комбіновані з Object.fromEntries()( ES10 / 2019 ):

const loopNestedObj = obj =>
  Object.fromEntries(
    Object.entries(obj).map(([key, val]) => {
      if (val && typeof val === "object") [key, loopNestedObj(val)]; // recurse
      else [key, updateMyVal(val)]; // or do something with key and val.
    })
  );

2
для вашого ES7, використовуючи приклад Object.entries, вам потрібно загорнути параметри функції стрілки [ключ, val] в дужки типу: `Object.entries (myObj) .forEach (([ключ, val]) => {/ * заяви * /}
puiu

6
Я думаю, було б корисно додати той факт, що Object.entries та Object.keys не повторюють прототип, що є великою різницею між ним та for у конструкції.
steviejay


95

Використання Underscore.js_.each :

_.each(validation_messages, function(value, key){
    _.each(value, function(value, key){
        console.log(value);
    });
});

4
Дякую Тіме, використовуючи підкреслення настільки добре, щоб мати швидкий і чистий варіант.
Кодер

56

Якщо ви використовуєте рекурсію, ви можете повернути властивості об'єкта будь-якої глибини,

function lookdeep(object){
    var collection= [], index= 0, next, item;
    for(item in object){
        if(object.hasOwnProperty(item)){
            next= object[item];
            if(typeof next== 'object' && next!= null){
                collection[index++]= item +
                ':{ '+ lookdeep(next).join(', ')+'}';
            }
            else collection[index++]= [item+':'+String(next)];
        }
    }
    return collection;
}

//example

var O={
    a:1, b:2, c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
};
var lookdeepSample= 'O={'+ lookdeep(O).join(',\n')+'}';


/*  returned value: (String)
O={
    a:1, 
    b:2, 
    c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
}

*/

2
Остерігайтеся циклів, як викликати це на вузлі DOM.
theazureshadow

45

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

I. Ключі потрібно отримати,

ofта Object.keysпідхід

let k;
for (k of Object.keys(obj)) {

    /*        k : key
     *   obj[k] : value
     */
}

inпідхід

let k;
for (k in obj) {

    /*        k : key
     *   obj[k] : value
     */
}

Використовуйте цей обережно, оскільки він може надрукувати властивості прототипу obj

✔ підхід ES7

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

}

Однак під час редагування я б не рекомендував метод ES7, оскільки JavaScript ініціалізує багато змінних внутрішньо для побудови цієї процедури (див. Відгуки для підтвердження). Якщо ви не розробляєте величезну програму, яка заслуговує на оптимізацію, це нормально, але якщо оптимізація є вашим пріоритетом, ви повинні подумати про це.

II. нам просто потрібно отримати доступ до кожного значення,

ofта Object.valuesпідхід

let v;
for (v of Object.values(obj)) {

}

Більше відгуків про тести:

  • Кешування Object.keysабо Object.valuesпродуктивність незначні

Наприклад,

const keys = Object.keys(obj);
let i;
for (i of keys) {
  //
}
// same as
for (i of Object.keys(obj)) {
  //
}
  • Наприклад Object.values, використання нативного forциклу з кешованими змінними у Firefox здається трохи швидшим, ніж використання for...ofциклу. Однак різниця не настільки важлива, і Chrome працює for...ofшвидше, ніж рідний forцикл, тому я б рекомендував використовувати в for...ofроботі Object.valuesв будь-яких випадках (4-й і 6-й тести).

  • У Firefox for...inцикл дійсно повільний, тому коли ми хочемо кешувати ключ під час ітерації, то краще використовувати Object.keys. Крім того, Chrome працює з обома структурами з однаковою швидкістю (перший та останній тести).

Ви можете перевірити тести тут: https://jsperf.com/es7-and-misc-loops


2
Приклад ES7 працює як шарм із React Native!
Тай Бейлі

Приємно пояснив. Спасибі
Алок

30

Я знаю, що пізно пізно, але мені знадобилося 2 хвилини, щоб написати цю оптимізовану та вдосконалену версію відповіді AgileJon:

var key, obj, prop, owns = Object.prototype.hasOwnProperty;

for (key in validation_messages ) {

    if (owns.call(validation_messages, key)) {

        obj = validation_messages[key];

        for (prop in obj ) {

            // using obj.hasOwnProperty might cause you headache if there is
            // obj.hasOwnProperty = function(){return false;}
            // but owns will always work 
            if (owns.call(obj, prop)) {
                console.log(prop, "=", obj[prop]);
            }

        }

    }

}

1
Чому ви зберігати hasOwnPropertyв ownsі потім викликати owns.call(obj, prop)замість того , щоб просто виклик , obj.hasOwnProperty(prop)як ця відповідь робить?
Рорі О'Кайн

14
Оскільки, objможливо, hasOwnPropertyфункція визначена на ній самостійно, тому вона не буде використовувати функцію з Object.prototype. Ви можете спробувати перед forциклом, як це, obj.hasOwnProperty = function(){return false;}і він не повторить жодне властивість.
Azder

4
@Azder +1 для відповіді та +1, якщо я міг би приємно про Object.prototype.hasOwnProperty. Я бачив це раніше у вихідному коді бібліотеки підкреслення, але не знаю, чому.
Самуїл

29
for(var k in validation_messages) {
    var o = validation_messages[k];
    do_something_with(o.your_name);
    do_something_else_with(o.your_msg);
}

14

p - значення

for (var key in p) {
  alert(key + ' => ' + p[key]);
}

АБО

Object.keys(p).forEach(key => { console.log(key, p[key]) })

9

У ES7 ви можете:

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

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


7

Кілька способів зробити це ...

1) 2 шари для ... у циклі ...

for (let key in validation_messages) {
   const vmKeys = validation_messages[key];
   for (let vmKey in vmKeys) {
      console.log(vmKey + vmKeys[vmKey]);
   }
}

2) ВикористанняObject.key

Object.keys(validation_messages).forEach(key => {
   const vmKeys = validation_messages[key];
   Object.keys(vmKeys).forEach(key => {
    console.log(vmKeys + vmKeys[key]);
   });
});

3) Рекурсивна функція

const recursiveObj = obj => {
  for(let key in obj){
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      console.log(key + obj[key]);
    } else {
      recursiveObj(obj[key]);
    }
  }
}

І називайте це так:

recursiveObj(validation_messages);

5

Ось вдосконалена та рекурсивна версія рішення AgileJon ( демонстрація ):

function loopThrough(obj){
  for(var key in obj){
    // skip loop if the property is from prototype
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      //your code
      console.log(key+" = "+obj[key]);
    } else {
      loopThrough(obj[key]);
    }
  }
}
loopThrough(validation_messages);

Це рішення працює для всіх видів різної глибини.


5

Ще один варіант:

var testObj = {test: true, test1: false};
for(let x of Object.keys(testObj)){
    console.log(x);
}

Я спробував ваше рішення в Chrome 55.0, і ви отримаєте помилку типу. Ваша відповідь виглядає приємно і лаконічно, якщо ви зможете працювати так, мабуть, це буде одним із кращих варіантів. Я спробував розібратися, але не розумію вашого рішення.
TolMera

2
@TolMera Виправлено.
чувак

4

Щойно завершений місяць тому ECMAScript-2017 вводить Object.values ​​(). Отже, тепер ви можете зробити це:

let v;
for (v of Object.values(validation_messages))
   console.log(v.your_name);   // jimmy billy

3

Я думаю, що варто зазначити, що jQuery це добре розбирає $.each().

Дивіться: https://api.jquery.com/each/

Наприклад:

$('.foo').each(function() {
    console.log($(this));
});

$(this)будучи єдиним предметом всередині об'єкта. Замініть $('.foo')на змінну, якщо ви не хочете використовувати селекторний механізм jQuery.


3

var obj={
name:"SanD",
age:"27"
}
Object.keys(obj).forEach((key)=>console.log(key,obj[key]));

Для циклу через JavaScript Object ми можемо використовувати forEach, а для оптимізації коду можемо використовувати функцію стрілки


2

Я не міг змусити вищезазначені пости зробити те, що я хотів.

Погравши з іншими відповідями тут, я зробив це. Це хакі, але це працює!

Для цього об’єкта:

var myObj = {
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"}
};

... цей код:

// Get every value in the object into a separate array item ...
function buildArray(p_MainObj, p_Name) {
    var variableList = [];
    var thisVar = "";
    var thisYes = false;
    for (var key in p_MainObj) {
       thisVar = p_Name + "." + key;
       thisYes = false;
       if (p_MainObj.hasOwnProperty(key)) {
          var obj = p_MainObj[key];
          for (var prop in obj) {
            var myregex = /^[0-9]*$/;
            if (myregex.exec(prop) != prop) {
                thisYes = true;
                variableList.push({item:thisVar + "." + prop,value:obj[prop]});
            }
          }
          if ( ! thisYes )
            variableList.push({item:thisVar,value:obj});
       }
    }
    return variableList;
}

// Get the object items into a simple array ...
var objectItems = buildArray(myObj, "myObj");

// Now use them / test them etc... as you need to!
for (var x=0; x < objectItems.length; ++x) {
    console.log(objectItems[x].item + " = " + objectItems[x].value);
}

... виробляє це в консолі:

myObj.pageURL = BLAH
myObj.emailBox.model = emailAddress
myObj.emailBox.selector = #emailAddress
myObj.passwordBox.model = password
myObj.passwordBox.selector = #password

0

Рішення, яке для мене працює, таке

_private.convertParams=function(params){
    var params= [];
    Object.keys(values).forEach(function(key) {
        params.push({"id":key,"option":"Igual","value":params[key].id})
    });
    return params;
}

0

Екзотичний - глибокий траверс

JSON.stringify(validation_messages,(field,value)=>{
  if(!field) return value;

  // ... your code

  return value;
})

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


-6

У моєму випадку (на основі попереднього) можлива будь-яка кількість рівнів.

var myObj = {
    rrr: undefined,
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"},
    proba: {odin:{dva:"rr",trr:"tyuuu"}, od:{ff:5,ppa:{ooo:{lll:'lll'}},tyt:'12345'}}
};


function lookdeep(obj,p_Name,gg){
    var A=[], tem, wrem=[], dd=gg?wrem:A;
    for(var p in obj){
        var y1=gg?'':p_Name, y1=y1 + '.' + p;
        if(obj.hasOwnProperty(p)){
           var tem=obj[p];
           if(tem && typeof tem=='object'){
               a1=arguments.callee(tem,p_Name,true);
               if(a1 && typeof a1=='object'){for(i in a1){dd.push(y1 + a1[i])};}
            }
            else{
               dd.push(y1 + ':' + String(tem));
            }
        }
    };
    return dd
};


var s=lookdeep(myObj,'myObj',false);
for (var x=0; x < s.length; ++x) {
console.log(s[x]+'\n');}

результат:

["myObj.rrr:undefined",
"myObj.pageURL:BLAH",
"myObj.emailBox.model:emailAddress",
"myObj.emailBox.selector:#emailAddress",
"myObj.passwordBox.model:password",
"myObj.passwordBox.selector:#password",
"myObj.proba.odin.dva:rr",
"myObj.proba.odin.trr:tyuuu",
"myObj.proba.od.ff:5",
"myObj.proba.od.ppa.ooo.lll:lll",
"myObj.proba.od.tyt:12345"]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.