【问题标题】:How do I enumerate the properties of a JavaScript object? [duplicate]如何枚举 JavaScript 对象的属性? [复制]
【发布时间】:2010-09-10 07:16:07
【问题描述】:

如何枚举 JavaScript 对象的属性?

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

【问题讨论】:

    标签: javascript properties


    【解决方案1】:

    足够简单:

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

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


    编辑:@bitwiseplatypus 是正确的,除非您使用 hasOwnProperty() 方法,否则您将获得继承的属性 - 但是,我不知道为什么熟悉面向对象编程的人会期望更少!通常,提出这个问题的人已经受到道格拉斯·克罗克福德的警告,这仍然让我有点困惑。同样,继承是 OO 语言的正常部分,因此是 JavaScript 的一部分,尽管它是原型的。

    现在,话虽如此,hasOwnProperty() 对过滤很有用,但我们不需要发出警告,就好像获取继承属性存在危险一样。

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

    【讨论】:

    • 我认为@bitwiseplatypus 只是在说“小心”。我的观点是 JavaScript 的 for/in 应该只在提及 hasOwnProperty() 和其他不那么明显的原型继承边缘案例的同时教授。
    • 有趣的是,这不适用于使用 Object.defineProperty 定义的 getter 属性,但适用于直接 getter。见我的jsfiddle
    • 我认为 大多数 人对 for(x in {}) 枚举任何东西感到惊讶,因为它看起来像一个简单的字典键迭代。这是非常出乎意料的,绝对值得特别警告。
    • 我想知道有多少人实际遇到过这个问题。我做过一次,而且很容易发现。教授 JS for in 循环时的警告很好,因此您可以根据需要过滤继承的属性。将我的for loops 与hasOwnProperty 盲支票乱扔以迎合对JS 没有基本了解的人是保证的。
    • @DavidSpector 猜测此消息指的是迭代器未实现对象属性的事实,仅适用于数组元素。您可能省略了 let/var 并写了 for (thing in set) { ... },这很容易做到。
    【解决方案2】:

    使用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() 没有任何缺点。

    【讨论】:

    • hasOwnProperty 的拥护者通常是那些 a) 遭受攻击性公共库的困扰 [例如:Prototype.js 大约 2003 年],或 b) 为异构系统构建了基础(想想:Web 门户)。其他人应该只是将 hasOwnProperty 包装在一个迭代器模式中,然后出去喝啤酒。
    • 这种模式是必要的并且被使用,因为 javascript 将“Actor 对象”与“哈希表”混为一谈。当对象是行为和特征的描述时,通常的规则适用,您想知道对象的属性而不关心它们是如何到达那里的。另一方面,如果您将对象用作键值存储,那么您很可能只对实际存储在对象上的实际键感兴趣。其他属性纯属巧合,并不代表对象的实际状态。其他语言通过不同的类型解决了这种区别。
    • 对于我作为一个js新手的理解,Object.prototype.baz = 'quux';如何改变myObject对象?
    【解决方案3】:

    已经多次提出的标准方式是:

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

    但是,Internet Explorer 6、7 和 8 在 JavaScript 解释器中存在一个错误,这会导致无法枚举某些键。如果你运行这段代码:

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

    If 将在除 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)已经实现了它。

    【讨论】:

      【解决方案4】:

      在现代浏览器 (ECMAScript 5) 中获取您可以执行的所有可枚举属性:

      Object.keys(obj) (查看链接以获取 sn-p 以了解旧浏览器的向后兼容性)

      或者获取不可枚举的属性:

      Object.getOwnPropertyNames(obj)

      Check ECMAScript 5 compatibility table

      附加信息: What is a enumerable attribute?

      【讨论】:

      • Object.getOwnPropertyNames(obj) 是您在处理某种错误对象(例如 TypeError)时想要的,因为这些属性是不可枚举的。
      • @vossad01 不幸的是getOwnPropertyNames 似乎不适用于ErrorEvent 对象:它只列出isTrusted。但是for( var pn in errEv ) console.log( pn ); } 确实 工作。这是令人沮丧和不一致的。
      【解决方案5】:

      我认为一个让我感到意外的案例是相关的:

      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 函数时可以不调用回调,这可能会使枚举属性中断。
      • 是的,我的意思是另一个脚本可以向对象原型添加任何内容。这些变化可能存在,也可能不存在,添加的方法可能会做任何事情。我并不认为这个 forEach 的例子很好——只是脚本可能会修改 for .. in 的结果以用于其他脚本。
      • 鉴于 JavaScript 开发的现代趋势,修改任何基本类型的原型对我来说似乎都是一个糟糕的决定;首先,还有其他方法可以让大多数猫剥皮,促使人们这样做。
      • 我同意,但如果其他代码这样做,我仍然不希望我的代码中断。
      • @cyberhobo - 明白,但是谁造成的?你?不,有人后来并没有仔细考虑或不知道他们正在做什么,因此他们是白痴。任何称职的 JavaScript 人都知道,修改任何最初不是他们的对象的原型都可能是在玩火。
      【解决方案6】:
      for (prop in obj) {
          alert(prop + ' = ' + obj[prop]);
      }
      

      【讨论】:

      • 最佳简单答案,解释了所有基本用法。
      • 添加obj[prop] #FTW
      【解决方案7】:

      简单的 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
      });
      

      【讨论】:

        【解决方案8】:

        以下是枚举对象属性的方法:

        var params = { name: 'myname', age: 'myage' }
        
        for (var key in params) {
          alert(key + "=" + params[key]);
        }
        

        【讨论】:

          【解决方案9】:

          我找到了...for (property in object) { // do stuff } 将列出所有属性,因此窗口对象上的所有全局声明变量..

          【讨论】:

            【解决方案10】:

            如果你使用Underscore.js库,你可以使用函数keys

            _.keys({one : 1, two : 2, three : 3});
            => ["one", "two", "three"]
            

            【讨论】:

            • Object.keys() 不是这样做的标准方法。
            【解决方案11】:

            您可以使用for of 循环。

            如果你想要一个数组使用:

            Object.keys(object1)

            参考。 Object.keys()

            【讨论】:

              【解决方案12】:

              Python 的 dict 有 'keys' 方法,这真的很有用。我认为在 JavaScript 中我们可以有这样的东西:

              function keys(){
                  var k = [];
                  for(var p in this) {
                      if(this.hasOwnProperty(p))
                          k.push(p);
                  }
                  return k;
              }
              Object.defineProperty(Object.prototype, "keys", { value : keys, enumerable:false });
              

              编辑:但@carlos-ruana 的答案非常有效。我测试了Object.keys(window),结果符合我的预期。

              5 年后编辑:扩展Object 不是一个好主意,因为它可能会与可能希望在其对象上使用keys 的其他库发生冲突,并且会导致您的项目出现不可预测的行为。 @carlos-ruana 答案是获取对象键的正确方法。

              【讨论】:

                【解决方案13】:

                如果您尝试枚举属性以便针对对象编写新代码,我建议您使用像 Firebug 这样的调试器来直观地查看它们。

                另一个方便的技术是使用 Prototype 的 Object.toJSON() 将对象序列化为 JSON,这将显示属性名称和值。

                var data = {name: 'Violet', occupation: 'character', age: 25, pets: ['frog', 'rabbit']};
                Object.toJSON(data);
                //-> '{"name": "Violet", "occupation": "character", "age": 25, "pets": ["frog","rabbit"]}'
                

                http://www.prototypejs.org/api/object/tojson

                【讨论】:

                  【解决方案14】:

                  我仍然是 JavaScript 的初学者,但我写了一个小函数来递归打印对象及其子对象的所有属性:

                  getDescription(object, tabs) {
                    var str = "{\n";
                    for (var x in object) {
                        str += Array(tabs + 2).join("\t") + x + ": ";
                        if (typeof object[x] === 'object' && object[x]) {
                          str += this.getDescription(object[x], tabs + 1);
                        } else {
                          str += object[x];
                        }
                        str += "\n";
                    }
                    str += Array(tabs + 1).join("\t") + "}";
                    return str;
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2017-03-05
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-07-12
                    • 1970-01-01
                    • 2013-02-04
                    相关资源
                    最近更新 更多