代码之家  ›  专栏  ›  技术社区  ›  Jason

javascript:检索对象属性名

  •  3
  • Jason  · 技术社区  · 16 年前

    我正在尝试编写一个函数,该函数需要知道正在传入的对象的属性名,如下所示:

    var data = { "key1":"value1", "key2":"value2", etc}
                   ^ i want the string value "key1"
    

    如何从中检索字符串“key1” data ?我知道我可以像 data[prop]=value 但我想知道 prop 来自传入的对象。

    如果这不合理,我想我可以试着解释更多。谢谢!

    我最终想做的事情是:

    for (var i = 0; i<data.length; i++)
    {
        var name = data[i].getPropertyName() <--- not a real function
        // do stuff
    }
    
    3 回复  |  直到 9 年前
        1
  •  21
  •   T.J. Crowder    9 年前

    在查看我们的选项之前,请快速记下有关javascript中属性的四个关键内容:

    1. 对象可以有自己的属性,以及从原型对象继承的属性。
    2. 属性可以是 可枚举的 不可枚举的 .
    3. 属性的名称可以是 或(截至ES2015/ES6)以下名称: Symbol S.
    4. 属性的名称不能是数字,如1。有时我们的行为就像他们做的那样,就像我们处理数组时那样,但他们没有。标准数组 aren't really arrays at all (根据规范,允许javascript实现在可能的时候进行优化),数组中的条目是名称为字符串的对象属性。所以 a = ['x', 'y', 'z'] 定义具有三个属性的数组,这些属性的名称为 "0" , "1" "2" . 当我们这样做 a[0] 要访问第一个,号码 0 转换为字符串。(理论上,同样,允许JavaScript实现进行优化。)

    可以发现和枚举所有这些属性(甚至是不可枚举的属性)。 您有几种选择:

    • for-in ( spec γ MDN )或不带 hasOwnProperty 在循环内部进行保护,以区分“自己的”属性和继承的属性。(不包括名为的属性 符号 循环通过 姓名 属性。
    • Object.keys ( spec γ MDN )(ES5+),返回对象自己可枚举属性的名称数组。(不包括名为的属性 符号 美国)
    • Object.getOwnPropertyNames ( spec γ MDN )(ES5+),返回对象自身属性的名称数组,而不管它们是否可枚举。(不包括名为的属性 符号 美国)
    • Reflect.enumerate ( spec γ MDN )(ES2015+)返回对象可枚举属性(包括其继承的属性)的名称的迭代器。(不包括名为的属性 符号 美国) 它在ES2016年被移除。
    • Object.getOwnPropertySymbols ( spec γ MDN )(ES2015+)返回对象自己属性的名称数组 符号 S,不管它们是否可以枚举。(去掉用字符串命名的。)
    • Reflect.ownKeys ( spec γ MDN )(ES2015+),返回对象自身属性的名称数组,无论它们是如何命名的。( 符号 或者字符串),以及它们是否可以枚举。

    如您所见,大多数操作只包含名称为字符串的属性,只有 Object.GetOwnPropertiesSymbols 反射键 给我们命名的 符号 S.

    密钥的顺序没有定义(甚至在ES2015中也没有定义) 为了在 关键字 . 在ES2015及以上版本中, 为其他四个定义,由 [[OwnPropertyKeys]] 和(如适用) [[Enumerate]] 操作。(由于ES2015(截至本文撰写之时)仍相对了解,因此可能并非所有的javascript引擎都正确地实现了订单。)

    让我们看看例子。首先,一些设置:

    // Create an object with one "own" property (plus the ones it
    // inherits from Object.prototype):
    var proto = {
        one: 1
    }
    
    // Create an object that uses the above as its prototype
    var obj = Object.create(proto);
    
    // Add a couple of enumerable properties
    obj.two = 2;
    obj.three = 3;
    
    // Add a non-enumerable property (by default, properties created
    // with Object.defineProperty are non-enumerable)
    Object.defineProperty(obj, "four", {
       value: 4,
       configurable: true,
       writable: true
    });
    

    ( Object.create 在ES5中添加了,但对于过时的JavaScript引擎(如IE8中的引擎),仅采用一个参数(如上所述)的版本很容易被填充/多道填充。 Object.defineProperty 在ES5中也添加了,无法正确填隙/填充。)

    由于大多数操作只涉及由字符串命名的属性,因此我们将忽略 符号 现在是S。

    一旦上面的代码运行,我们在内存中就有了这个 * 名称旁边表示它是不可枚举的属性):

                                                        +−−−−−−−−−−−−−−−−−+
    Object.prototype−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| toString*       |−−>(...a function...)
                                                    |   | valueOf*        |−−>(...a function...)
                                                    |   | hasOwnProperty* |−−>(...a function...)
                                                    |   | ...             |
                                                    |   +−−−−−−−−−−−−−−−−−+
                                                    |
                                 +−−−−−−−−−−−−−−−+  |
    proto−−−−−−−−−−−−−−−−−−−−+−−>| [[Prototype]] |−−+
                             |   | one: 1        |
                             |   +−−−−−−−−−−−−−−−+
                             |
          +−−−−−−−−−−−−−−−+  |
    obj−−>| [[Prototype]] |−−+
          | two: 2        |
          | three: 3      |
          | four*: 4      |
          +−−−−−−−−−−−−−−−+
    

    有了这个设置,我们来看看我们的选项:

    为了在

    为了在 循环访问对象所有属性的名称(包括从原型继承的属性),这些属性的名称是字符串(不包括任何名称 符号 s)。

    for (var name in obj) {
        // name is the name of each property, so:
        console.log(name + " = " + obj[name]);
    }
    

    // Create an object with one "own" property (plus the ones it
    // inherits from Object.prototype):
    var proto = {
        one: 1
    }
    
    // Create an object that uses the above as its prototype
    var obj = Object.create(proto);
    
    // Add a couple of enumerable properties
    obj.two = 2;
    obj.three = 3;
    
    // Add a non-enumerable property (by default, properties created
    // with Object.defineProperty are non-enumerable)
    Object.defineProperty(obj, "four", {
       value: 4,
       configurable: true,
       writable: true
    });
    
    for (var name in obj) {
        // name is the name of each property, so:
        console.log(name + " = " + obj[name]);
    }

    我们看到了

    two = 2
    three = 3
    one = 1
    

    …或类似的。您在中看到的属性顺序没有定义,甚至在ES2015中也没有。

    为了在 用一个 哈桑特性 警卫

    如果需要循环,但忽略继承的属性,则可以添加 哈桑特性 检查:

    for (var name in obj) {
        if (obj.hasOwnProperty(name)) {
            console.log(name + " = " + obj[name]);
        }
    }
    

    // Create an object with one "own" property (plus the ones it
    // inherits from Object.prototype):
    var proto = {
        one: 1
    }
    
    // Create an object that uses the above as its prototype
    var obj = Object.create(proto);
    
    // Add a couple of enumerable properties
    obj.two = 2;
    obj.three = 3;
    
    // Add a non-enumerable property (by default, properties created
    // with Object.defineProperty are non-enumerable)
    Object.defineProperty(obj, "four", {
       value: 4,
       configurable: true,
       writable: true
    });
    
    for (var name in obj) {
        if (obj.hasOwnProperty(name)) {
            console.log(name + " = " + obj[name]);
        }
    }

    我们看到了

    two = 2
    three = 3
    

    我们看不见 one 因为它是遗传的。

    自从 哈桑特性 是对象上的一个方法,理论上它可以被一个没有返回我们期望的内容的版本覆盖:

    obj.hasOwnProperty = function() {
        return true;
    };
    

    …这会愚弄我们上面的循环。这通常不是一个问题,但是如果你想防范它,可以使用 Object.prototype 而是:

    var hasOwn = Object.prototype.hasOwnProperty;
    for (var name in obj) {
        if (hasOwn.call(obj, name)) {
            console.log(name + " = " + obj[name]);
        }
    }
    

    当然,有人可以分配给 Object.prototype.hasOwnProperty 房地产也一样,但如果他们这样做,相当多的事情可能会破裂。

    关键字 (ES5+,易于填隙/填充)

    关键字 给我们一个对象的名称数组 自身的,可枚举的 用字符串命名的属性。因此,它不包括继承的属性、标记为不可枚举的属性或命名为的属性 符号 S.

    var propNames = Object.keys(obj);
    

    然后,我们可以用几种方法中的任何一种循环该数组中的条目,例如 forEach :

    propNames.forEach(function(name) {
        console.log(name + " = " + obj[name]);
    });
    

    // Create an object with one "own" property (plus the ones it
    // inherits from Object.prototype):
    var proto = {
        one: 1
    }
    
    // Create an object that uses the above as its prototype
    var obj = Object.create(proto);
    
    // Add a couple of enumerable properties
    obj.two = 2;
    obj.three = 3;
    
    // Add a non-enumerable property (by default, properties created
    // with Object.defineProperty are non-enumerable)
    Object.defineProperty(obj, "four", {
       value: 4,
       configurable: true,
       writable: true
    });
    
    var propNames = Object.keys(obj);
    propNames.forEach(function(name) {
        console.log(name + " = " + obj[name]);
    });

    通过我们的示例设置,我们可以:

    二=2
    三=3
    

    规范没有定义数组中名称的顺序,甚至在ES2015中也没有。

    对象.getownprotynames (ES5+)

    对象.getownprotynames 返回用字符串命名的对象自身属性的名称数组,无论是否可枚举。它去掉了以命名的属性 符号 S.

    var propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function(name) {
        console.log(name + " = " + obj[name]);
    });
    

    // Create an object with one "own" property (plus the ones it
    // inherits from Object.prototype):
    var proto = {
        one: 1
    }
    
    // Create an object that uses the above as its prototype
    var obj = Object.create(proto);
    
    // Add a couple of enumerable properties
    obj.two = 2;
    obj.three = 3;
    
    // Add a non-enumerable property (by default, properties created
    // with Object.defineProperty are non-enumerable)
    Object.defineProperty(obj, "four", {
       value: 4,
       configurable: true,
       writable: true
    });
    
    var propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function(name) {
        console.log(name + " = " + obj[name]);
    });

    通过我们的示例设置,我们可以:

    two = 2
    three = 3
    four = 4
    

    数组中名称的顺序由 [ownpropyeys]公司 规范中的操作,即订单为:

    • 限定为 integer indexes ,按数字顺序。
    • 其他属性的名称,按创建该属性的顺序。

    所以我们得到 two , three , four 因为这些都不符合规范对整数索引的定义,这就是我们添加属性的顺序。如果我们以不同的顺序添加它们,或者包括那些合格的索引,我们将得到不同的结果:

    var obj2 = {};
    obj2.four = 4;
    obj2[0] = "zero";
    obj2.two = 2;
    obj2.three = 3;
    Object.getOwnPropertyNames(obj2).forEach(function(name) {
        console.log(name + " = " + obj2[name]);
    });
    

    var obj2 = {};
    obj2.four = 4;
    obj2[0] = "zero";
    obj2.two = 2;
    obj2.three = 3;
    Object.getOwnPropertyNames(obj2).forEach(function(name) {
        console.log(name + " = " + obj2[name]);
    });

    给我们:

    0 = zero
    four = 4
    two = 2
    three = 3
    

    是第一个,因为尽管它是一个字符串,但它符合整数索引的条件。然后我们得到 因为它是先创建的,然后 然后 .

    反射.枚举 (ES2015) 在ES2016中删除

    反射.枚举 在ES2016年被移除。

    反射.枚举 使用新的 迭代器 ES2015的特点。它返回一个迭代器,该迭代器将遍历名为对象的可枚举属性(包括继承属性)的字符串的名称,跳过名为 符号 或者那是不可数的。首先,它访问“自己”的属性(按 [ownpropyeys]公司 ,然后继承属性(除非它们被“自己的”属性隐藏)。

    我们可以用新的 for-of 循环到循环:

    for (let name of Reflect.enumerate(obj)) {
        console.log(name + " = " + obj[name]);
    }
    

    通过我们的设置,我们可以:

    二=2
    三=3
    一=1
    

    最后是因为它是继承的,而“拥有”属性是第一位的。

    注意:同样,由于ES2015在本文中是相对较新的,因此一些JavaScript引擎可能无法实现 Reflect 对象。

    Object.GetOwnPropertiesSymbols (ES2015)

    Object.GetOwnPropertiesSymbols 返回对象自身属性的数组,该属性的名称为 符号 S,不管它们是否可以枚举。(不包括用字符串命名的)我们需要一个不同的设置来尝试这个,因为我们没有包括 符号 -主设置中的命名属性。由于继承的属性被忽略,我们将保持简单:

    var obj3 = {};
    obj3[Symbol("x")] = "ecks";
    obj3[1] = "one";
    obj3[Symbol("y")] = "why";
    obj3.z = "zee";
    Object.getOwnPropertySymbols(obj3).forEach(function(symbol) {
        console.log(symbol.toString() + " = " + obj3[symbol]);
    });
    

    var obj3 = {};
    obj3[Symbol("x")] = "ecks";
    obj3[1] = "one";
    obj3[Symbol("y")] = "why";
    obj3.z = "zee";
    Object.getOwnPropertySymbols(obj3).forEach(function(symbol) {
        console.log(symbol.toString() + " = " + obj3[symbol]);
    });

    输出:

    Symbol(x) = ecks
    Symbol(y) = why
    

    z 未列出,因为它有字符串名称,而不是 符号 姓名。 Symbol(x) 因为它是先创建的。

    还有很多事情要做 Symbols 比这里显示的要多,但是你可以看到,如果我们给一个符号一个名字,我们可以使用 toString 回来 Symbol(the name here) 作为字符串。有意思,我们得打电话 弦线 明确地 ( thesymbol.toString() 或使用 String(theSymbol ; + 在向字符串追加符号时,运算符不会为我们执行此操作。

    反射键 (ES2015)

    反射键 返回对象自身属性的数组,无论这些属性如何命名( 符号 或者字符串),以及它们是否可以枚举。它忽略继承的属性:

    var obj3 = {};
    obj3[Symbol("x")] = "ecks";
    obj3[1] = "one"
    obj3[Symbol("y")] = "why";
    obj3.z = "zee";
    Reflect.ownKeys(obj3).forEach(function(key) {
        console.log(key.toString() + " = " + obj3[key]);
    });
    

    var obj3 = {};
    obj3[Symbol("x")] = "ecks";
    obj3[1] = "one"
    obj3[Symbol("y")] = "why";
    obj3.z = "zee";
    Reflect.ownKeys(obj3).forEach(function(key) {
        console.log(key.toString() + " = " + obj3[key]);
    });

    输出:

    1 = one
    z = zee
    Symbol(x) = ecks
    Symbol(y) = why
    

    注意顺序,它由 [ownpropyeys]公司 : 1 是第一个,因为它是一个字符串,可以作为整数索引。 Z 是下一个,因为它是一个名为property的字符串。然后我们有了 符号 -命名属性,按创建顺序排列。

        2
  •  5
  •   Robusto    10 年前
    var data = { "key1":"value1", "key2":"value2"}; //etc
    
    for (var prop in data) {
      var propName = prop;
      var propVal = data[prop];
      // do something with your new variables
    }
    

    是的,就是这么简单。

        3
  •  1
  •   adam bahdaj    10 年前

    受以上答案的启发,我想知道如何在继承链上列出任何对象(包括Emumerated和Not)的所有属性。 这个功能似乎足够了。

    var allPropNames = [];
    
    function digAllPropsOut (ob){
        var propNames = Object.getOwnPropertyNames(ob);
        propNames.forEach(function(name) {
            allPropNames.push(name);
        });
        // allPropNames.push('\n');
        ob = Object.getPrototypeOf(ob);
        if (ob)
            digAllPropsOut(ob);
    }