如何枚举 JavaScript 对象的属性?

725

如何枚举JavaScript对象的属性?

我实际上想列出所有已定义变量及其值,但我了解到定义变量实际上会创建一个window对象的属性。

14个回答

896

足够简单:

for(var propertyName in myObject) {
   // propertyName is what you want
   // you can get the value like this: myObject[propertyName]
}

现在,你无法通过这种方式获取私有变量,因为它们不可用。


编辑:@bitwiseplatypus是正确的,除非您使用hasOwnProperty()方法,否则会获取继承的属性 - 然而,我不知道任何熟悉面向对象编程的人会期望得到其他结果!通常,提出这个问题的人已经受到了Douglas Crockford对此的警告,这仍然使我有些困惑。再次强调,继承是OO语言的正常部分,因此也是JavaScript的一部分,尽管它是基于原型的。

那么,hasOwnProperty()确实有用于过滤,但我们不需要发出警告,好像获取继承的属性有什么危险。

编辑2:@bitwiseplatypus提出了这样一种情况:如果有人在比您最初编写对象的时候(通过其原型)后的某个时间点添加属性/方法到您的对象中,那么将会发生什么情况 - 虽然这可能导致意外行为,但我个人并不认为这完全是我的问题。这只是一个观点问题。此外,如果我设计的方式在构建对象期间使用原型,并且仍然有代码迭代对象的属性,并且我想要所有继承的属性,那该怎么办?我将不使用hasOwnProperty()。然后,假设某人以后添加了新属性。如果出现问题,那是我的错吗?我不这么认为。我认为这就是为什么jQuery(例如)已指定了扩展其工作方式的方法(通过jQuery.extendjQuery.fn.extend)。


7
我认为@bitwiseplatypus只是在说“小心”。我认为JavaScript的for/in应该在提到hasOwnProperty()和其他不太明显的原型继承边缘情况的同时进行教学。 - pcorcoran
29
我认为大多数人都会对for(x in {})枚举任何内容感到惊讶,因为它看起来只是一个简单的字典键迭代。这非常意外,并且绝对值得特别警告。 - Glenn Maynard
4
我想知道有多少人实际上遇到了这个 bug。我遇到过一次,而且很容易发现。在教授 JS 的 for in 循环时提供警告是有益的,这样你就可以过滤继承的属性(如果需要的话)。为了迎合那些没有基本 JS 理解的人,给我的 for loop 填充盲目的 hasOwnProperty 检查是不必要的。 - Ruan Mendes
这个答案最初是在10年前写的。事物会发生变化,因此像这样涉及任何东西的方法应该被检查以确保持续的准确性。我想这是StackOverflow最明显的限制之一 - 在许多情况下,数据会过时。 :) - Jason Bunting
1
@DavidSpector 猜测这条信息是指对象属性没有实现迭代器,只有数组元素才有。你可能忘记写let/var,而是写了for (thing in set) { ... },这很容易犯的错误。 - Peter Wone
显示剩余5条评论

235
使用for..in循环枚举对象的属性,但要小心。枚举将返回被枚举对象的属性,也包括任何父对象的原型属性。
var myObject = {foo: 'bar'};

for (var name in myObject) {
  alert(name);
}

// results in a single alert of 'foo'

Object.prototype.baz = 'quux';

for (var name in myObject) {
  alert(name);
}

// results in two alerts, one for 'foo' and one for 'baz'

为避免在枚举过程中包含继承属性,请使用hasOwnProperty()进行检查:
for (var name in myObject) {
  if (myObject.hasOwnProperty(name)) {
    alert(name);
  }
}

编辑:我不同意JasonBunting的观点,认为我们不需要担心枚举继承属性。因为如果你不期望地枚举继承属性,那么它会改变代码的行为,存在危险性。

无论这个问题在其他语言中是否存在,事实是它确实存在,并且JavaScript特别容易受到影响,因为对对象原型的修改会影响到子对象,即使是在实例化之后进行的修改也是如此。

这就是为什么JavaScript提供了hasOwnProperty(),并且这也是为什么你应该使用它,以确保第三方代码(或任何可能修改原型的代码)不会破坏你的代码。除了添加一些额外的代码字节,使用hasOwnProperty()没有任何负面影响。


4
专注于hasOwnProperty的追随者通常是那些a)曾遭受过攻击性常用库(例如:原型.js约2003年)的人,或b)为异构系统(考虑:Web门户)打下了基础。其他人只需要将hasOwnProperty包装在迭代器模式中并出去喝点啤酒即可。 - pcorcoran
31
这种模式是必要的并被使用,因为JavaScript将“Actor对象”与“哈希表”混淆在一起。当对象是行为和特征的描述时,通常规则适用,您想了解对象上的属性而不关心它们是如何得到的。另一方面,如果您将对象用作键值存储,则最有可能只对实际存储在对象上的键感兴趣。其他属性是偶然的,不代表对象的实际状态。其他语言通过不同的类型解决这种区别。 - SingleNegationElimination
作为一个 JavaScript 新手,Object.prototype.baz = 'quux'; 语句会如何改变 myObject 对象? - JHBonarius
现在尝试评估 myObject.baz :) 属性访问将沿着原型链向上查找,直到找到匹配项或原型链结束,因此它会在 Object.prototype 上找到 baz 并返回它。另外,我意识到这个答案是从2008年的,但我建议现在使用 Object.hasOwn(myObject, name) - Ian Kim

52

3
如果你正在处理某种错误对象(例如TypeError),那么Object.getOwnPropertyNames(obj)就是你想要的,因为它可以获取非可枚举属性。 - vossad01
@vossad01 很不幸,getOwnPropertyNames 似乎无法与 ErrorEvent 对象一起使用:它只列出了 isTrusted。然而,for( var pn in errEv ) console.log( pn ); } 确实可以工作。这很令人沮丧和不一致。 - Dai

50

标准的方法,已经被多次提出,即:

for (var name in myObject) {
  alert(name);
}

然而,Internet Explorer 6、7和8的JavaScript解释器存在一个漏洞,导致某些键无法枚举。如果您运行此代码:

var obj = { toString: 12};
for (var name in obj) {
  alert(name);
}

除IE以外的所有浏览器都将弹出“12”。 IE将简单地忽略此键。 受影响的键值包括:

  • isPrototypeOf
  • hasOwnProperty
  • toLocaleString
  • toString
  • valueOf

要确保在IE中安全,请使用以下内容:

for (var key in myObject) {
  alert(key);
}

var shadowedKeys = [
  "isPrototypeOf",
  "hasOwnProperty",
  "toLocaleString",
  "toString",
  "valueOf"
];
for (var i=0, a=shadowedKeys, l=a.length; i<l; i++) {
  if map.hasOwnProperty(a[i])) {
    alert(a[i]);
  }
}

好消息是,EcmaScript 5 定义了 Object.keys(myObject) 函数,它将对象的键作为数组返回,并且某些浏览器(如Safari 4)已经实现了它。


27

我认为一个让我感到惊讶并且很有代表性的案例会很有帮助:

var myObject = { name: "Cody", status: "Surprised" };
for (var propertyName in myObject) {
  document.writeln( propertyName + " : " + myObject[propertyName] );
}

但是让我惊讶的是,输出结果为

name : Cody
status : Surprised
forEach : function (obj, callback) {
    for (prop in obj) {
        if (obj.hasOwnProperty(prop) && typeof obj[prop] !== "function") {
            callback(prop);
        }
    }
}

为什么会出现这种情况?页面上的另一个脚本已经扩展了Object原型:

Object.prototype.forEach = function (obj, callback) {
  for ( prop in obj ) {
    if ( obj.hasOwnProperty( prop ) && typeof obj[prop] !== "function" ) {
      callback( prop );
    }
  }
};

有趣的是,这意味着在重新定义forEach函数时可以不调用回调函数,这可能会导致枚举属性失败。 - davenpcj
考虑到JavaScript开发的现代趋势,修改任何基本类型的原型对我来说似乎是一个不明智的决定;在大多数情况下,有其他方法可以解决人们首先想要这样做的问题。 - Jason Bunting
1
我同意,但如果其他代码这样做了,我仍然不希望我的代码出现问题。 - cyberhobo
@cyberhobo - 我明白了,但是是谁导致它出问题的?是你吗?不是,是那些在你之后来的人,他们没有仔细考虑或者不知道自己要做什么,因此他们是白痴。任何值得一提的 JavaScript 程序员都知道修改任何原本不属于他们的对象的原型可能会玩火。 - Jason Bunting
似乎越来越多的开发人员意识到了这个错误。如果我的例子被证明完全不相关,那我会很高兴,新的开发人员学习如何枚举对象时甚至不需要意识到这种可能性。 - cyberhobo
显示剩余2条评论

17
for (prop in obj) {
    alert(prop + ' = ' + obj[prop]);
}

最好的简单答案,解释了基本使用所需的一切。 - TetraDev
obj[prop]的加法 #FTW - Joe Johnston

12

这是如何列举对象属性的方法:

var params = { name: 'myname', age: 'myage' }

for (var key in params) {
  alert(key + "=" + params[key]);
}

11

简单的JavaScript代码:

for(var propertyName in myObject) {
   // propertyName is what you want.
   // You can get the value like this: myObject[propertyName]
}

jQuery:

jQuery.each(obj, function(key, value) {
   // key is what you want.
   // The value is in: value
});

9
我发现for (property in object) { // do stuff }会列出所有属性,因此也会列出窗口对象上的所有全局声明变量。

8

您可以使用for循环。

如果您想要一个数组,请使用:

Object.keys(object1)

参考:Object.keys()


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