如何遍历或枚举 JavaScript 对象?

我有一个如下的 JavaScript 对象:

var p = {
    "p1": "value1",
    "p2": "value2",
    "p3": "value3"
};

现在,我想遍历所有p元素( p1p2p3 ...),并获取它们的键和值。我怎样才能做到这一点?

我可以根据需要修改 JavaScript 对象。我的最终目标是遍历一些键值对,如果可能的话,我想避免使用eval

答案

您可以使用for-in循环,如其他人所示。但是,您还必须确保获得的键是对象的实际属性,而不是来自原型。

这是代码段:

var p = {
    "p1": "value1",
    "p2": "value2",
    "p3": "value3"
};

for (var key in p) {
    if (p.hasOwnProperty(key)) {
        console.log(key + " -> " + p[key]);
    }
}

用 Object.keys()替代:

var p = {
    0: "value1",
    "b": "value2",
    key: "value3"
};

for (var key of Object.keys(p)) {
    console.log(key + " -> " + p[key])
}

请注意,使用for-of而不是for-in ,如果不使用它将在命名属性上返回 undefined,并且Object.keys()确保仅使用对象自己的属性,而不使用整个原型链属性

在 ECMAScript 5 下,您可以组合Object.keys()Array.prototype.forEach()

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

Object.keys(obj).forEach(function(key) {
    console.log(key, obj[key]);
});

ECMAScript 6 for...of添加for...of

for (const key of Object.keys(obj)) {
    console.log(key, obj[key]);
}

ECMAScript 8 添加了Object.entries() ,从而避免了必须在原始对象中查找每个值:

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

您可以结合for...of ,destructuring 和Object.entries

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

Object.keys()Object.entries()以与for...in循环相同的顺序迭代属性, 但忽略原型链 。仅迭代对象自身的可枚举属性。

您必须使用for-in 循环

但是在使用这种循环时要非常小心,因为这会沿原型链循环所有属性

因此,在使用 for-in 循环时,请始终使用hasOwnProperty方法来确定迭代中的当前属性是否确实是您要检查的对象的属性:

for (var prop in p) {
    if (!p.hasOwnProperty(prop)) {
        //The current property is not a direct property of p
        continue;
    }
    //Do your logic with the property here
}

如果我们不提及循环对象的替代方法,那么这个问题将是不完整的。

如今,许多知名的 JavaScript 库都提供了自己的方法来遍历集合,即遍历数组对象类似数组的对象 。这些方法使用方便,并且与任何浏览器完全兼容。

  1. 如果使用jQuery ,则可以使用jQuery.each()方法。它可以用来无缝地遍历对象和数组:

    $.each(obj, function(key, value) {
        console.log(key, value);
    });
  2. Underscore.js你可以找到方法_.each()它遍历元素的列表,每个收益依次为提供的功能(要注意在iteratee函数参数的顺序!):

    _.each(obj, function(value, key) {
        console.log(key, value);
    });
  3. Lo-Dash提供了几种遍历对象属性的方法。基本的_.forEach() (或别名_.each() )对于循环遍历对象和数组很有用,但是具有length属性的(!)对象被视为数组,并且为避免这种现象,建议使用_.forIn()_.forOwn()方法(这些方法也首先使用value参数):

    _.forIn(obj, function(value, key) {
        console.log(key, value);
    });

    _.forIn()遍历对象的自身和继承的可枚举属性,而_.forOwn()仅遍历对象的自身属性(基本上检查hasOwnProperty函数)。对于简单的对象和对象文字,这些方法中的任何一种都可以正常工作。

通常,所有描述的方法对于任何提供的对象都具有相同的行为。除了使用 native for..in循环通常比任何抽象方法jQuery.each()例如jQuery.each() 都快 ,这些方法更易于使用,需要更少的编码并提供更好的错误处理。

在 ECMAScript 5 中,您可以在文字的迭代字段中使用新方法Object.keys

您可以在MDN 上看到更多信息

下面是我的选择,它是当前版本浏览器(Chrome30,IE10,FF25)中的一种更快的解决方案

var keys = Object.keys(p),
    len = keys.length,
    i = 0,
    prop,
    value;
while (i < len) {
    prop = keys[i];
    value = p[prop];
    i += 1;
}

您可以将这种方法的性能与jsperf.com上的不同实现进行比较

您可以在Kangax 的 compat 表上看到的浏览器支持

对于旧的浏览器,您可以使用简单完整的 polyfill

UPD:

perfjs.info上此问题中所有最流行情况的性能比较:

对象文字迭代

您可以像这样遍历它:

for (var key in p) {
  alert(p[key]);
}

请注意, key将不采用属性的值,而只是索引值。

前言:

  • 对象属性可以是自己的 (属性在对象本身上),也可以是继承的 (不在对象本身上,在其原型之一上)。
  • 对象属性可以是可枚举的不可枚举的 。不可枚举的属性被排除在许多属性枚举 / 数组之外。
  • 属性名称可以是字符串或符号。名称为 Symbol 的属性被排除在许多属性枚举 / 数组之外。

在 2018 年,您可以使用以下选项遍历对象的属性(以下示例如下所示):

  1. for-in [ MDNspec ] —一个循环结构,循环遍历对象的可枚举属性的名称,包括名称为字符串的继承属性
  2. Object.keys [ MDNspec ]- 一个函数,提供对象自己的可枚举的属性名称(名称为字符串)的数组。
  3. Object.values [ MDNspec ] —一个提供对象自己的 可枚举属性的的数组的函数。
  4. Object.entries [ MDNspec ] —一个提供对象自己的 可枚举属性的名称值的数组的函数(该数组中的每个条目都是[name, value]数组)。
  5. Object.getOwnPropertyNames [ MDNspec ] —一个提供名称为字符串的对象自身属性(甚至是不可枚举的属性)名称数组的函数。
  6. Object.getOwnPropertySymbols [ MDNspec ] —一种函数,提供名称为 Symbols 的对象自身属性(甚至是不可枚举的属性)名称的数组。
  7. Reflect.ownKeys [ MDNspec ] —一个函数,提供对象自身属性的名称(甚至是不可枚举的属性)名称的数组,无论这些名称是字符串还是符号。
  8. 如果要使用对象的所有属性,包括不可枚举的继承属性,则需要在对象中的每个对象上使用循环和Object.getPrototypeOf [ MDNspec ] 并使用Object.getOwnPropertyNamesObject.getOwnPropertySymbolsReflect.ownKeys 。原型链(此答案底部的示例)。

for-in外,它们都for-in数组上使用某种循环构造( forfor-offorEach等)。

例子:

for-in

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const name in o) {
    const value = o[name];
    console.log(`${name} = ${value}`);
}

Object.keys (带有for-of循环,但是您可以使用任何循环构造)

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const name of Object.keys(o)) {
    const value = o[name];
    console.log(`${name} = ${value}`);
}

Object.values

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const value of Object.values(o)) {
    console.log(`${value}`);
}

Object.entries

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const [name, value] of Object.entries(o)) {
    console.log(`${name} = ${value}`);
}

Object.getOwnPropertyNames

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const name of Object.getOwnPropertyNames(o)) {
    const value = o[name];
    console.log(`${name} = ${value}`);
}

Object.getOwnPropertySymbols

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const name of Object.getOwnPropertySymbols(o)) {
    const value = o[name];
    console.log(`${String(name)} = ${value}`);
}

Reflect.ownKeys

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (const name of Reflect.ownKeys(o)) {
    const value = o[name];
    console.log(`${String(name)} = ${value}`);
}

所有属性 ,包括继承的不可枚举的属性

// A prototype object to inherit from, with a string-named property
const p = {answer: 42};
// The object we'll look at, which inherits from `p`
const o = Object.create(p);
// A string-named property
o.question = "Life, the Universe, and Everything";
// A symbol-named property
o[Symbol("author")] = "Douglas Adams";
for (let depth = 0, current = o; current; ++depth, current = Object.getPrototypeOf(current)) {
    for (const name of Reflect.ownKeys(current)) {
        const value = o[name];
        console.log(`[${depth}] ${String(name)} = ${String(value)}`);
    }
}
.as-console-wrapper {
  max-height: 100% !important;
}

由于 es2015 越来越流行,因此我发布了此答案,其中包括使用 generator 和 iterator 来平滑地迭代[key, value]对。 Ruby 等其他语言也有可能。

好的,这是一个代码:

const MyObject = {
  'a': 'Hello',
  'b': 'it\'s',
  'c': 'me',
  'd': 'you',
  'e': 'looking',
  'f': 'for',
  [Symbol.iterator]: function* () {
    for (const i of Object.keys(this)) {
      yield [i, this[i]];
    }
  }
};

for (const [k, v] of MyObject) {
  console.log(`Here is key ${k} and here is value ${v}`);
}

有关如何执行迭代器和生成器的所有信息,可以在开发人员 Mozilla 页面上找到。

希望它可以帮助某人。

编辑:

ES2017 将包含Object.entries ,这将使对象中的[key, value]对迭代更加容易。现在已经知道,根据ts39阶段信息,它将成为标准的一部分。

我认为是时候更新我的答案,让它变得比现在更新鲜了。

const MyObject = {
  'a': 'Hello',
  'b': 'it\'s',
  'c': 'me',
  'd': 'you',
  'e': 'looking',
  'f': 'for',
};

for (const [k, v] of Object.entries(MyObject)) {
  console.log(`Here is key ${k} and here is value ${v}`);
}

您可以在MDN页面上找到有关用法的更多信息

for(key in p) {
  alert( p[key] );
}

注意:您可以在数组上执行此操作,但是也将遍历length和其他属性。

在查看完所有答案之后,我自己的用法不需要 hasOwnProperty,因为我的 json 对象是干净的;添加任何额外的 javascript 处理确实没有任何意义。这就是我正在使用的:

for (var key in p) {
    console.log(key + ' => ' + p[key]);
    // key is key
    // value is p[key]
}