如何以对象为成员循环遍历纯 JavaScript 对象

Posted

技术标签:

【中文标题】如何以对象为成员循环遍历纯 JavaScript 对象【英文标题】:How to loop through a plain JavaScript object with the objects as members 【发布时间】:2010-10-29 15:18:52 【问题描述】:

如何遍历 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"
    

【问题讨论】:

【参考方案1】:
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]);
    

【讨论】:

Internet Explorer 不同意 (sigh),当您执行 obj[prop] 时说“对象不支持此属性或方法”。我还没有找到解决方案。 @MildFuzz 实际上,如果您认为 JS 对象不需要具有数字键,那么这是有道理的。你不能只遍历一个对象。 JS 的for in 与传统的foreach 非常相似。 for...in 是一个很好的解决方案,但是如果你在 for() 循环中使用 Promise 时要小心,因为如果你在循环中创建了一个 var,你就不能在承诺的 then 功能。你在循环中的 var 只存在一次,所以它在每个 then 函数中都相同,即使是最后一个值。如果您有这个问题,请尝试“Object.keys(obj).forEach”或我在下面的答案。 hasOwnProperty 对于现代浏览器(IE9 +)几乎总是多余的。【参考方案2】:

在 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]);

);

【讨论】:

+1 是为了代码简洁,但显然它的执行效率不如 for 令人惊讶。 JSPerf - for in vs Object.keys 使用这种方法要小心这个错误:“TypeError: Object.keys called on non-object”。 for ... in ... hasOwnProperty 模式可以在任何东西上调用,据我所知(对象、数组、null、未定义、true、false、数字原语、对象)。 @techiev2 那些测试从未有效。查看我更新的当前性能状态:jsperf.com/objdir/20 @techiev2:不是Object.keys() 让它变慢,而是forEach() 和对.length 的重复访问!如果您改用经典的 for-loop,它几乎是 Firefox 33 中 for..in + hasOwnProperty() 的两倍。 @techiev2 这一点都不奇怪,Axel Rauschmayer 的方法是使用keys 函数和forEach 函数,它需要解析匿名函数,然后在每个元素上调用匿名函数forEach 循环。如果您了解编程,您就会明白所有这些解析和函数调用比 for 结构循环之类的原生解决方案花费的时间要多得多。【参考方案3】:

ES6/2015 中,您可以循环访问这样的对象(使用 arrow function):

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

JS Bin

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

如果您还想循环遍历嵌套对象,可以使用 recursive 函数 (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.
  );
;

JS Bin

与上面的函数相同,但使用 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.
    )
  );

循环对象的另一种方法是使用 for ... infor ... of。见vdegenne's nicely written answer。

【讨论】:

对于使用 Object.entries 的 ES7 示例,您需要将箭头函数参数 [key,val] 括在括号中,例如:`Object.entries(myObj).forEach(([key, val] ) => /* 语句*/ 我认为添加 Object.entries 和 Object.keys 不会迭代原型这一事实会很有用,这是它与 for in 构造之间的最大区别。【参考方案4】:

这个问题

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

【讨论】:

简而言之:在 for-in 循环中检查 hasOwnProperty 请注意,仅当您的对象具有原型方法时才需要这样做。例如,如果您循环的对象只是一个 JSON 对象,则不需要此检查。 @rednaw 为了安全起见,我使用该检查,因为 Object.prototype 可以修改。没有理智的脚本会这样做,但是您无法通过疯狂的浏览器扩展控制可能在您的页面中运行的脚本。浏览器扩展在您的页面中运行(在大多数浏览器上),它们可能会导致奇怪的问题(例如,将 window.setTimeout 设置为 null!)。【参考方案5】:

使用Underscore.js’s _.each:

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

【讨论】:

感谢蒂姆,使用下划线绝对是个不错的选择。【参考方案6】:

如果你使用递归,你可以返回任意深度的对象属性-

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


*/

【讨论】:

小心循环,比如在 DOM 节点上调用它。【参考方案7】:

此答案是本文中提供的解决方案的汇总 发布一些performance feedbacks。我认为有两个 用例和OP没有提到他是否需要访问密钥才能使用它们 在循环过程中。

I.需要访问密钥

ofObject.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.我们只需要访问每个值

ofObject.values 方法

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



更多关于测试的反馈:

缓存 Object.keysObject.values 性能可以忽略不计

例如,

const keys = Object.keys(obj);
let i;
for (i of keys) 
  //

// same as
for (i of Object.keys(obj)) 
  //

对于Object.values 的情况,在Firefox 中使用带有缓存变量的本机for 循环似乎比使用for...of 循环要快一些。但是,区别并不那么重要,并且 Chrome 运行 for...of 比原生 for 循环快,所以我建议在任何情况下(第 4 次和第 6 次测试)处理 Object.values 时使用 for...of

在 Firefox 中,for...in 循环非常慢,所以当我们想在迭代期间缓存密钥时,最好使用Object.keys。此外,Chrome 以相同的速度运行这两种结构(第一次和最后一次测试)。

你可以在这里查看测试:https://jsperf.com/es7-and-misc-loops

【讨论】:

【参考方案8】:
for(var k in validation_messages) 
    var o = validation_messages[k];
    do_something_with(o.your_name);
    do_something_else_with(o.your_msg);

【讨论】:

【参考方案9】:

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

【讨论】:

为什么将hasOwnProperty 存储在owns 中,然后调用owns.call(obj, prop) 而不是像this answer 那样只调用obj.hasOwnProperty(prop) 因为obj 可能在其自身上定义了hasOwnProperty 函数,因此它不会使用来自Object.prototype 的函数。您可以在 for 这样的循环之前尝试 obj.hasOwnProperty = function()return false; 并且它不会遍历任何属性。 @Azder +1 作为答案,如果我可以的话 +1 对于 Object.prototype.hasOwnProperty 的好处。我以前在下划线库的源代码中看到过,但不知道为什么。【参考方案10】:

p 是值

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

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

【讨论】:

【参考方案11】:

在 ES7 中你可以这样做:

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

【讨论】:

我做了一些测试,这种方法在处理大量数据时确实很慢。【参考方案12】:
for(var key in validation_messages)
    for(var subkey in validation_messages[key])
        //code here
        //subkey being value, key being 'yourname' / 'yourmsg'
    

【讨论】:

【参考方案13】:

有几种方法可以做到这一点...

1)两层for...in循环...

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

【讨论】:

【参考方案14】:

另一种选择:

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

【讨论】:

我在 Chrome 55.0 中尝试了您的解决方案,但您收到类型错误。您的答案看起来不错且简洁,如果您可以使其正常工作,它可能是更好的选择之一。我试图弄清楚,但不明白你的解决方案。【参考方案15】:

AgileJon 解决方案的改进和递归版本来了 (demo):

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

此解决方案适用于各种不同的深度。

【讨论】:

【参考方案16】:

ECMAScript 2017,一个月前刚刚定稿,引入了 Object.values()。所以现在你可以这样做了:

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

【讨论】:

【参考方案17】:

我认为值得指出的是,jQuery 使用 $.each() 很好地解决了这个问题。

见:.each()

示例:

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

$(this) 是对象内的单个项目。如果您不想使用 jQuery 的选择器引擎,请将 $('.foo') 替换为变量。

【讨论】:

【参考方案18】:

我无法让之前的答案完全符合我的要求。

在玩弄了这里的其他回复之后,我做了这个。这很 hacky,但它有效!

对于这个对象:

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

【讨论】:

【参考方案19】:

var obj = 
    name: "SanD",
    age: "27"

Object.keys(obj).forEach((key) => console.log(key,obj[key]));

要遍历 JavaScript Object,我们可以使用 forEach 并优化代码,我们可以使用箭头函数。

【讨论】:

【参考方案20】:
var validation_messages = 
    "key_1": 
        "your_name": "jimmy",
        "your_msg": "hello world"
    ,
    "key_2": 
        "your_name": "billy",
        "your_msg": "foo equals bar"
    

for (var i in validation_messages) 
    console.log("i = \"" + i + "\"");
    console.log("validation_messages[\"" + i + "\"] = ");
    console.log(validation_messages[i]);
    console.log("\n");
    for (var j in validation_messages[i]) 
        console.log("j = \"" + j + "\"");
        console.log("validation_messages[\"" + i + "\"][\"" + j + "\"] = \"" + validation_messages[i][j] + "\"");
        console.log("\n");
    
    console.log('\n');

输出:

i = "key_1"
validation_messages["key_1"] = 

  your_name:"jimmy",
  your_msg:"hello world"


j = "your_name"
validation_messages["key_1"]["your_name"] = "jimmy"

j = "your_msg"
validation_messages["key_1"]["your_msg"] = "hello world"


i = "key_2"
validation_messages["key_2"] = 

  your_name:"billy",
  your_msg:"foo equals bar"


j = "your_name"
validation_messages["key_2"]["your_name"] = "billy"

j = "your_msg"
validation_messages["key_2"]["your_msg"] = "foo equals bar"

【讨论】:

【参考方案21】:

使用 ES8 Object.entries() 应该是一种更紧凑的方式来实现这一点。

Object.entries(validation_messages).map(([key,object]) => 

    alert(`Looping through key : $key`);

    Object.entries(object).map(([token, value]) => 
        alert(`$token : $value`);
    );
);

【讨论】:

【参考方案22】:

适合我的解决方案如下:

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

【讨论】:

【参考方案23】:

forEach2

(发现here):

var lunch = 
    sandwich: 'ham',
    age: 48,
;
lunch.forEach2(function (item, key) 
    console.log(key);
    console.log(item);
);

代码:

if (!Object.prototype.forEach2) 
    Object.defineProperty(Object.prototype, 'forEach2', 
        value: function (callback, thisArg) 
            if (this == null) 
                throw new TypeError('Not an object');
            
            thisArg = thisArg || window;
            for (var key in this) 
                if (this.hasOwnProperty(key)) 
                    callback.call(thisArg, this[key], key, this);
                
            
        
    );

【讨论】:

【参考方案24】:

异域一号-深度遍历

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

  // ... your code

  return value;
)

在这个解决方案中,我们使用replacer,它允许深度遍历整个对象和嵌套对象 - 在每个级别上,您将获得所有字段和值。如果您需要获取每个字段的完整路径,请查看here。

var validation_messages = 
    "key_1": 
        "your_name": "jimmy",
        "your_msg": "hello world"
    ,
    "key_2": 
        "your_name": "billy",
        "your_msg": "foo equals bar",
        "deep": 
          "color": "red",
          "size": "10px"
        
    


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

  console.log(`key: $field.padEnd(11) - value: $value`);

  return value;
)

【讨论】:

【参考方案25】:

2020 年你想要不可变和通用的函数

这将遍历由子对象、数组和字符串组成的多维对象并应用自定义函数:

export const iterate = (object, func) => 
  const entries = Object.entries(object).map(([key, value]) =>
    Array.isArray(value)
      ? [key, value.map(e => iterate(e, func))]
      : typeof value === 'object'
      ? [key, iterate(value, func)]
      : [key, func(value)]
  );
  return Object.fromEntries(entries);
;

用法:

const r = iterate(data, e=>'converted_'+e);
console.log(r);

【讨论】:

【参考方案26】:

在我的情况下(基于前面的),任何数量的级别都是可能的。

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"]

【讨论】:

以上是关于如何以对象为成员循环遍历纯 JavaScript 对象的主要内容,如果未能解决你的问题,请参考以下文章

我可以以相反的顺序循环遍历 javascript 对象吗?

JavaScript循环遍历

es6 javascript的Iterator 和 for...of 循环

如何循环遍历对象数组并根据 JavaScript 中的条件添加新的对象键?

javaScript for in循环遍历对象

在javascript中循环遍历数组中的对象