วิธีการวนซ้ำวัตถุ JavaScript ธรรมดากับวัตถุเป็นสมาชิกได้อย่างไร


1599

ฉันจะวนลูปผ่านสมาชิกทั้งหมดในวัตถุ JavaScript รวมถึงค่าที่เป็นวัตถุได้อย่างไร

ตัวอย่างเช่นฉันจะวนซ้ำสิ่งนี้ได้อย่างไร (เข้าถึง "your_name" และ "your_message" สำหรับแต่ละรายการ)

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

11
สำเนาซ้ำของลูปผ่านวัตถุ 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 ไม่เห็นด้วย ( ถอนหายใจ ) กล่าวว่า "วัตถุไม่สนับสนุนคุณสมบัติหรือวิธีการนี้" เมื่อคุณทำ obj [prop] ฉันยังไม่พบวิธีแก้ปัญหานี้
user999717

2
@MildFuzz จริง ๆ แล้วมันสมเหตุสมผลถ้าคุณคิดว่าวัตถุ JS นั้นไม่จำเป็นต้องมีปุ่มตัวเลข คุณไม่สามารถวนซ้ำวัตถุ JS for inนั้นคล้ายกับแบบดั้งเดิมforeachมาก
Jake Wilson

4
for ... in เป็นคำตอบที่ดี แต่ถ้าคุณใช้สัญญาใน for () - loop ต้องระวังเพราะถ้าคุณสร้าง var ใน loop คุณไม่สามารถใช้มันในสัญญา 'แล้ว - ฟังก์ชัน คุณ var ในลูปมีอยู่เพียงครั้งเดียวดังนั้นจึงมีในทุกฟังก์ชั่นนั้นเหมือนกันแม้กระทั่งค่าสุดท้าย หากคุณมีปัญหาดังกล่าวให้ลอง "Object.keys (obj) .forEach" หรือคำตอบของฉันด้านล่าง
Biber

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 - สำหรับใน vs Object.keys
techiev2

6
ระวังข้อผิดพลาดนี้โดยใช้วิธีนี้: "TypeError: Object.keys เรียกว่าไม่ใช่วัตถุ" for ... in ... hasOwnPropertyรูปแบบที่สามารถเรียกร้องอะไรเท่าที่ผมสามารถบอกได้ (วัตถุอาร์เรย์โมฆะไม่ได้กำหนดจริงเท็จจำนวนดั้งเดิม, วัตถุ)
theazureshadow

2
โปรดทราบว่า IE7 ไม่รองรับสิ่งนี้
Paul D. Waite

3
@ techiev2 การทดสอบเหล่านั้นไม่เคยถูกต้อง ดูรายการที่อัปเดตของฉันสำหรับสถานะปัจจุบันของผลการดำเนินงาน: jsperf.com/objdir/20
OrganicPanda

4
@ techiev2: มันไม่ได้Object.keys()ทำให้ช้ามันค่อนข้างforEach()และการเข้าถึงซ้ำแล้วซ้ำอีก.length! หากคุณใช้คลาสสิก - forลูปแทนมันจะเร็วเป็นสองเท่าใน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 เป็น null!)
robocat

1
ขอบคุณมาก
รถรางสีน้ำเงิน

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 (([key, val]) => {/ * คำสั่ง * /}
puiu

6
ฉันคิดว่ามันจะมีประโยชน์ในการเพิ่มความจริงที่ว่า Object.entries และ Object.keys ไม่ได้ย้ำผ่านต้นแบบซึ่งเป็นความแตกต่างใหญ่ระหว่างมันและสำหรับในการสร้าง
steviejay

ขอบคุณมาก
รถรางสีน้ำเงิน


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 กรณีการใช้งานและ OP ไม่ได้พูดถึงว่าเขาต้องการเข้าถึงคีย์เพื่อใช้ในระหว่างกระบวนการวนรอบ

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 เริ่มต้นตัวแปรจำนวนมากภายในเพื่อสร้างขั้นตอนนี้ (ดูคำติชมเพื่อรับการพิสูจน์) หากคุณไม่ได้พัฒนาแอพขนาดใหญ่ที่สมควรได้รับการปรับให้เหมาะสมมันก็โอเค แต่ถ้าการเพิ่มประสิทธิภาพเป็นเรื่องสำคัญที่สุดคุณควรคิดถึงมัน

ครั้งที่สอง เราแค่ต้องเข้าถึงแต่ละค่า

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เช่นการใช้ Native forloop กับตัวแปรแคชใน Firefox น่าจะเร็วกว่าการใช้for...ofloop อย่างไรก็ตามความแตกต่างนั้นไม่สำคัญและ Chrome นั้นทำงานfor...ofได้เร็วกว่า native forloop ดังนั้นฉันขอแนะนำให้ใช้for...ofเมื่อต้องรับมือกับObject.valuesทุกกรณี (การทดสอบครั้งที่ 4 และครั้งที่ 6)

  • ใน Firefox ที่ห่วงจริงๆช้าดังนั้นเมื่อเราต้องการที่จะแคชที่สำคัญในระหว่างการซ้ำมันจะดีกว่าที่จะใช้for...in Object.keysPlus Chrome กำลังเรียกใช้โครงสร้างทั้งสองที่ความเร็วเท่ากัน (ทดสอบครั้งที่ 1 และครั้งสุดท้าย)

คุณสามารถตรวจสอบการทดสอบได้ที่นี่: https://jsperf.com/es7-and-misc-loops


2
ตัวอย่าง ES7 ทำงานได้อย่างมีเสน่ห์ด้วย React Native!
Ty Bailey

อธิบายอย่างดี ขอบคุณ
Alok Ranjan

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)ตามคำตอบนี้?
Rory O'Kane

14
เพราะobjอาจมีฟังก์ชั่นที่กำหนดไว้ในนั้นด้วยตนเองจึงจะไม่ใช้หนึ่งจากhasOwnProperty Object.prototypeคุณสามารถลองก่อนการforวนซ้ำเช่นนี้obj.hasOwnProperty = function(){return false;}และมันจะไม่วนซ้ำในคุณสมบัติใด ๆ
Azder

4
@Azder +1 สำหรับคำตอบและ +1 ถ้าฉันสามารถทำได้เพื่อสิ่งที่ดีเกี่ยวกับ Object.prototype.hasOwnProperty ฉันเห็นว่าก่อนหน้านี้ภายในซอร์สโค้ดของไลบรารีขีดล่าง แต่ไม่ทราบสาเหตุ
ซามูเอล


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

ฉันทำการทดสอบวิธีนี้ช้ามากเมื่อจัดการกับข้อมูลจำนวนมาก
vdegenne


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 เราสามารถใช้ 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;
})

ในการแก้ปัญหานี้เราใช้replacerซึ่งอนุญาตให้สำรวจทั้งวัตถุและวัตถุที่ซ้อนกันในระดับลึก - ในแต่ละระดับคุณจะได้รับฟิลด์และค่าทั้งหมด หากคุณต้องการเส้นทางแบบเต็มไปยังแต่ละฟิลด์ดูที่นี่


-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"]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.