如何迭代JavaScript对象?

632

我在JavaScript中有一个对象:

{
    abc: '...',
    bca: '...',
    zzz: '...',
    xxx: '...',
    ccc: '...',
    // ...
}

我想使用一个for循环来获取它的属性。并且我想分部迭代它(不是一次性迭代所有对象属性)。

对于一个简单的数组,我可以用标准的for循环来做到这一点:

for (i = 0; i < 100; i++) { ... } // first part
for (i = 100; i < 300; i++) { ... } // second
for (i = 300; i < arr.length; i++) { ... } // last

但是如何用对象实现它呢?


32
请记住,对象属性不是按顺序存储的。当您遍历对象时,无法保证它们出现的顺序。 - James Allardice
@JamesAllardice的评论是来自2013年的。自2015年起,对象现在具有定义的迭代顺序。 - undefined
19个回答

1

这真是让人感到头疼,因为它不属于标准的Javascript。

/**
 * Iterates the keys and values of an object.  Object.keys is used to extract the keys.
 * @param object The object to iterate
 * @param fn (value,key)=>{}
 */
function objectForEach(object, fn) {
    Object.keys(object).forEach(key => {
        fn(object[key],key, object)
    })
}

注意:我已经将回调参数切换为(value,key),并添加了第三个对象,以使API与其他API保持一致。
使用方法如下。
const o = {a:1, b:true};
objectForEach(o, (value, key, obj)=>{
    // do something
});

1
因为你在第一句话中的陈述而得到了赞同。尽管如果将值作为第一个参数,索引或键作为第二个参数,对象作为第三个参数会更像数组的forEach()函数,但这样做会更好。我建议推荐使用lodash。 - CONTRACT SAYS I'M RIGHT
我确实喜欢(值,键)顺序的想法。这也是像Vue这样的库所使用的方式。因为对象是上下文,我认为它应该作为第一个参数。这在函数式编程中非常标准。 - Steven Spungin
如果不是因为ECMA-262将数组定义为具有forEach()、map()、reduce()、filter()的对象,这些方法都接收回调函数并按顺序接收[value,index,array]参数,我会同意这里的观点。在JS中,对象可以被理解为另一种集合;然后这些方法在[value,key | index,context]参数上变得统一(这就是lodash和underscore正在做的)。在我看来,这个“统一的集合”协议更加强大。此外,对象不是上下文:您可以为回调设置任何您喜欢的this,因为回调有自己的上下文。 - CONTRACT SAYS I'M RIGHT
也许我应该使用“接收器”这个词,而不是这个。无论如何,仍然很麻烦;我希望参数可以以任何顺序传入。 - Steven Spungin
哦,我明白我们可能误解了彼此。 我一直在评论回调参数及其顺序,而不是实际的objectForEach函数。 如果这让你感到困惑,很抱歉。 - CONTRACT SAYS I'M RIGHT

1

我最终想出了一个方便的实用函数,它具有统一的界面来迭代对象、字符串、数组、类型化数组、映射、集合(任何可迭代对象)。

const iterate = require('@a-z/iterate-it');
const obj = { a: 1, b: 2, c: 3 };

iterate(obj, (value, key) => console.log(key, value)); 
// a 1
// b 2
// c 3

https://github.com/alrik/iterate-javascript


1
如果您想一次迭代整个对象,可以使用 for in 循环:
for (var i in obj) {
  ...
}

但是,如果你想将对象分成部分,实际上你是无法做到的。不能保证对象中的属性按任何指定顺序排列。因此,我可以想到两个解决方案。

第一个解决方案是“删除”已读取的属性:

var i = 0;
for (var key in obj) {
    console.log(obj[key]);
    delete obj[key];
    if ( ++i > 300) break;
}

另一个我能想到的解决方案是使用数组的数组而不是对象:
var obj = [['key1', 'value1'], ['key2', 'value2']];

那么,标准的for循环就可以工作。


1

在对象迭代中,我们通常使用for..in循环。这个结构将循环遍历所有可枚举的属性,包括通过原型继承继承的属性。例如:

let obj = {
  prop1: '1',
  prop2: '2'
}

for(let el in obj) {
  console.log(el);
  console.log(obj[el]);
}

然而,for..in 循环将遍历所有可枚举元素,这不允许我们将迭代分成块。为了实现此目的,我们可以使用内置的 Object.keys() 函数将一个对象的所有键检索到一个数组中。然后,我们可以将迭代分成多个 for 循环,并使用键数组访问属性。例如:

let obj = {
  prop1: '1',
  prop2: '2',
  prop3: '3',
  prop4: '4',
};

const keys = Object.keys(obj);
console.log(keys);


for (let i = 0; i < 2; i++) {
  console.log(obj[keys[i]]);
}


for (let i = 2; i < 4; i++) {
  console.log(obj[keys[i]]);
}


1

是的,你可以使用for循环来遍历一个对象。这里是一个例子

var myObj = {
    abc: 'ABC',
    bca: 'BCA',
    zzz: 'ZZZ',
    xxx: 'XXX',
    ccc: 'CCC',
}

var k = Object.keys (myObj);
for (var i = 0; i < k.length; i++) {
    console.log (k[i] + ": " + myObj[k[i]]);
}

注意:上述示例仅适用于IE9+。请参见此处了解Object.keys浏览器支持情况。


1
这是一个手工制作的解决方案:
function iterationForObject() {
    let base = 0,
        Keys= Object.keys(this);
    return {
        next: () => {
            return {
                value: {
                    "key": Keys[base],
                    "value": this[Keys[base]]
                },
                done: !(base++ < Keys.length)
            };
        }
    };
}
Object.prototype[Symbol.iterator] = iterationForObject;

然后您可以循环任何对象:
for ( let keyAndValuePair of (Object Here) ) {
    console.log(`${keyAndValuePair.key} => ${keyAndValuePair.value}`);
}

1
<script type="text/javascript">
// method 1
var images = {};
images['name'] = {};
images['family'] = {};
images[1] = {};
images['name'][5] = "Mehdi";
images['family'][8] = "Mohammadpour";
images['family']['ok'] = 123456;
images[1][22] = 2602;
images[1][22] = 2602;
images[1][22] = 2602;
images[1][22] = 2602;
images[1][23] = 2602;

for (const [key1, value1] of Object.entries(images)){
    for (const [key2, value2] of Object.entries(value1)){
        console.log(`${key1} => ${key2}: ${value2}`);
    }
}


console.log("=============================");

// method 2
var arr = [];
for(var x = 0; x < 5; x++){
     arr[x] = [];    
     for(var y = 0; y < 5; y++){ 
         arr[x][y] = x*y;    
     }    
 }

for(var i = 0; i < arr.length; i++) {
    var cube = arr[i];
    for(var j = 0; j < cube.length; j++) {
        console.log("cube[" + i + "][" + j + "] = " + cube[j]);
    }
}

</script>

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

1
实际上不是这样的。这意味着Object是有序的。它们并不是。只有因为实现细节,所以“如果你能理解这个句子,它就起作用”才有效。这根本不能保证工作。此外,您不应该将函数和变量命名为TitleCase。那是给类使用的。 - Florian Wendelborn

0
你可以尝试使用 lodash- 一个现代化的 JavaScript 工具库,提供模块化、高效和扩展功能 来快速迭代对象:

var  users  =   {
    'fred':     { 
        'user':   'fred',
            'age':  40 
    },
    'pebbles':  { 
        'user':   'pebbles',
         'age':  1 
    }
}; 
_.mapValues(users,  function(o)  { 
    return  o.age; 
});
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
// The `_.property` iteratee shorthand.
console.log(_.mapValues(users,  'age')); // returns age property & value 
console.log(_.mapValues(users,  'user')); // returns user property & value 
console.log(_.mapValues(users)); // returns all objects 
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.js"></script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接