Javascript:检索对象属性名称 [英] Javascript: Retrieve Object Property Names

查看:52
本文介绍了Javascript:检索对象属性名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to write a function that needs to know the property names of an object being passed in, like so:

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

如何从数据中检索字符串key1?我知道我可以动态设置属性,如 data [prop] = value 但我想知道 prop 来自哪个传入的对象。

How do I retrieve the string "key1" from data? I know I can set a property dynamically like data[prop]=value but i want to know what prop is from an object passed in.

如果这没有意义,我想我可以尝试解释更多。谢谢!

If that doesn't make sense I suppose I could try to explain more. Thanks!

我最终想做的事情如下:

I eventually want to do something like:

for (var i = 0; i<data.length; i++)
{
    var name = data[i].getPropertyName() <--- not a real function
    // do stuff
}


推荐答案

在我们看看之前选项,快速了解JavaScript中属性的四个关键内容:

Before we look at our options, a quick note of the four key things about properties in JavaScript:


  1. 对象可以拥有自己的属性,以及它们继承的属性他们的原型对象。

  2. 属性可以是可枚举不可枚举

  3. 属性可以具有字符串或(从ES2015 / ES6开始)名称符号 s。

  4. 属性不能有数字的名称,例如1.有时候我们会像他们那样行事,就像我们处理数组时一样,但事实并非如此。标准数组根本不是真正的数组(根据规范) ;允许JavaScript实现在可以的情况下进行优化),并且数组中的条目是名称为字符串的对象属性。所以 a = ['x','y','z'] 定义一个包含三个属性的数组,其名称为012。当我们 a [0] 访问第一个时,数字 0 将转换为字符串。 (理论上;再次允许JavaScript实现进行优化。)

  1. Objects can have properties of their own, and properties they inherit from their prototype object.
  2. Properties can be enumerable or non-enumerable.
  3. Properties can have names that are strings, or (as of ES2015/ES6) names that are Symbols.
  4. Properties cannot have names that are numbers, like 1. Sometimes we act like they do, as when we're dealing with arrays, but they don't. Standard arrays aren't really arrays at all (per the spec; JavaScript implementations are allowed to optimize when they can), and the entries in arrays are object properties whose names are strings. So a = ['x', 'y', 'z'] defines an array with three properties whose names are "0", "1", and "2". When we do a[0] to access the first one, the number 0 is converted to a string. (In theory; again, the JavaScript implementation is allowed to optimize.)

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

All of these properties can be discovered and enumerated (even the non-enumerable ones). You have several options for doing so:


  • for-in loop spec | MDN ),有或没有 hasOwnProperty 保护循环内部以区分自己的和继承的属性。 (不包括以符号 s命名的属性。)循环显示属性的名称

  • Object.keys spec | MDN )(ES5 +),它返回一个对象自己的可枚举属性名称的数组。 (不包括以符号 s命名的属性。)

  • Object.getOwnPropertyNames spec | MDN )( ES5 +),它返回一个对象自己的属性名称数组,无论它们是否可枚举。 (不包括以符号 s命名的属性。)

  • Reflect.enumerate 规范 | MDN )(ES2015 +),它返回一个对象的可枚举属性名称的迭代器,包括它继承的属性。 (不包括以符号 s命名的属性。)它已在ES2016中删除。

  • Object.getOwnPropertySymbols spec | MDN )(ES2015 +),返回一个对象自己的属性名称数组,以 Symbol 命名,无论它们是否可枚举。 (不包括用字符串命名的。)

  • Reflect.ownKeys spec | MDN )(ES2015 +),它返回一个对象自己属性名称的数组无论它们如何命名( Symbol 或字符串),以及它们是否可枚举。

  • A for-in loop (spec | MDN), with or without a hasOwnProperty guard inside the loop to differentiate between "own" and inherited properties. (Does not include properties named with Symbols.) Loops through the names of the properties.
  • Object.keys (spec | MDN) (ES5+), which returns an array of the names of an object's own, enumerable properties. (Does not include properties named with Symbols.)
  • Object.getOwnPropertyNames (spec | MDN) (ES5+), which returns an array of the names of an object's own properties, regardless of whether they're enumerable. (Does not include properties named with Symbols.)
  • Reflect.enumerate (spec | MDN) (ES2015+), which returns an iterator for the names of the enumerable properties of an object, including ones it inherits. (Does not include properties named with Symbols.) It was removed in ES2016.
  • Object.getOwnPropertySymbols (spec | MDN) (ES2015+), which returns an array of the names of an object's own properties named with Symbols, regardless of whether they're enumerable. (Leaves out ones named with strings.)
  • Reflect.ownKeys (spec | MDN) (ES2015+), which returns an array of the names of an object's own properties no matter how they're named (Symbol or string), and whether they're enumerable or not.

如您所见,大多数操作只包括名称为字符串的属性,只有 Object.getOwnPropertySymbols Reflect.ownKeys 给我们以符号 s命名的那些。

As you can see, most of the operations only include properties whose names are strings, with only Object.getOwnPropertySymbols and Reflect.ownKeys giving us the ones named with Symbols.

顺序对于 for-in Object.keys ,未定义键(甚至在ES2015中)。在ES2015及以上版本中,通过 为其他四个定义 -ordinary-object-internal-methods-and-internal-slots-ownpropertykeysrel =noreferrer> [[OwnPropertyKeys]] 和(如适用) [[Enumerate]] 操作。 (由于ES2015仍然[在撰写本文时]相对知道,可能并非所有JavaScript引擎都能正确实现订单。)

The order of the keys is not defined (not even in ES2015) for for-in or Object.keys. In ES2015 and above, it is defined for the other four, by the [[OwnPropertyKeys]] and (where applicable) [[Enumerate]] operations. (Since ES2015 is still [as of this writing] relatively knew, it's possible not all JavaScript engines correctly implement the order yet.)

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

Let's look at examples. First, some setup:

// 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中,无法正确填充/填充。)

(Object.create was added in ES5, but the version of it taking just one argument [as above] can easily be shimmed/polyfilled for obsolete JavaScript engines, like the one in IE8. Object.defineProperty was also added in ES5, and cannot be correctly shimmed/polyfilled.)

由于大多数操作只涉及字符串命名的属性,我们忽略了现在符号

Since most of the operations only involve properties named by strings, we're ignoring Symbols for now.

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

Once the code above runs, we have this in memory (the * next to a name indicates it's a non-enumerable property):


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

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

With that setup, let's look at our options:

for-in 循环遍历所有对象属性的名称(包括它从其原型继承的属性),其名称为字符串(省略任何名称为符号 s)。

for-in loops through the names of all of an object's properties (including ones it inherits from its prototype) whose names are strings (leaving out any whose names are Symbols).

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

...或者simila河您看到属性的顺序未定义,即使在ES2015中也没有定义。

...or similar. The order you see the properties in is not defined, not even in ES2015.

如果我们想要循环但忽略继承属性,我们可以添加 hasOwnProperty check:

If we wanted the loop, but ignoring inherited properties, we can add a hasOwnProperty check:

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

我们不喜欢看不到一个因为它是继承的。

We don't see one because it's inherited.

因为 hasOwnProperty 是对象的一个​​方法,理论上它可以用一个没有返回我们期望的版本来覆盖:

Since hasOwnProperty is a method on the object, in theory it could be overridden with a version that didn't return what we expect:

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

...这会欺骗我们的循环。这通常不是问题,但如果你想防范它,请使用 Object.prototype 中的那个:

...which would fool our loop above. That's not usually an issue, but if you want to guard against it, use the one from Object.prototype instead:

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

当然,有人可以分配给 Object.prototype.hasOwnProperty 属性,但如果他们这样做,相当多的事情可能会破坏。

Of course, someone can assign to the Object.prototype.hasOwnProperty property as well, but if they do, a fair number of things are likely to break.

Object.keys 为我们提供了一个对象的拥有的,可枚举的属性的名称数组,这些属性用字符串命名。因此它不包括继承的属性,标记为非可枚举的属性,或以符号 s命名的属性。

Object.keys gives us an array of the names of the object's own, enumerable properties named with strings. So it doesn't include inherited properties, properties marked non-enumerable, or properties named with Symbols.

var propNames = Object.keys(obj);

然后,我们可以通过多种方式循环遍历该数组中的条目,例如 forEach

We can then loop over the entries in that array in any of several ways, such as 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]);
});

通过我们的样本设置,它给了我们:

With our sample setup, that gives us:


two = 2
three = 3

订单o f数组中的名称未由规范定义,即使在ES2015中也没有定义。

The order of the names in the array is not defined by the specification, not even in ES2015.

Object.getOwnPropertyNames 返回以字符串命名的对象自己的属性名称数组,无论是否可枚举或不。它省略了以 Symbol s命名的属性。

Object.getOwnPropertyNames returns an array of the names of the object's own properties named with strings, whether enumerable or not. It leaves out properties named with Symbols.

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]);
});

通过我们的样本设置,它给了我们:

With our sample setup, that gives us:


two = 2
three = 3
four = 4

数组中名称的顺序由 [[OwnPropertyKeys]] 操作在规范中,该订单是:

The order of the names in the array is defined by the [[OwnPropertyKeys]] operation in the specification, which says the order is:


  • 符合整数索引,按数字顺序排列。

  • 其他属性的名称,按照创建属性的顺序。

  • The property names that qualify as integer indexes, in numeric order.
  • The names for other properties, in the order the property was created.

所以我们得到两个三个四个因为这些都不符合规范对整数索引的定义,这就是订单我们在其中添加了属性。如果我们以不同的顺序添加它们,或者包含那些符合索引的顺序,我们会得到不同的结果:

So we get two, three, four because none of those fits the spec's definition of an integer index, and that's the order in which we added the properties. If we added them in a different order, or included ones that qualified as indexes, we'd get different results:

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

0 首先是因为虽然它是一个字符串,它符合整数索引的标准。然后我们得到,因为它是先创建的,然后两个,然后

0 was first because although it's a string, it fits the criteria for an integer index. Then we get four because it was created first, then two, then three.

Reflect.enumerate 已在ES2016中删除。

Reflect.enumerate was removed in ES2016.


Reflect.enumerate 使用新的迭代器功能ES2015。它返回一个迭代器,它将遍历对象的字符串命名的可枚举属性的名称,包括继承的属性,跳过以 Symbol 命名的或不可枚举的。首先,它访问自己的属性(按照 [[OwnPropertyKeys]] 定义的顺序,然后继承属性(除非它们被自己的属性隐藏) 。

Reflect.enumerate uses the new iterator feature of ES2015. It returns an iterator that will iterate over the names of the string-named enumerable properties of the object, including inherited ones, skipping ones named with Symbol or that aren't enumerable. First it visits the "own" properties (in the order defined by [[OwnPropertyKeys]], and then inherited properties (unless they've been hidden by "own" properties).

我们可以使用新的 for-of 循环来循环它们:

We can use the new for-of loop to loop over them:

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

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

With our setup, that gives us:


two = 2
three = 3
one = 1

一个在最后,因为它是继承的,而自己的属性是第一个。

one is at the end because it's inherited, and "own" properties come first.

注意:同样,由于ES2015在撰写本文时相对较新,因此一些JavaScript引擎可能尚未实现 Reflect 对象。

Note: Again, since ES2015 is relatively new as of this writing, some JavaScript engines may not implement the Reflect object yet.

Object.getOwnPropertySymbols 返回一个对象自己的属性数组使用符号 s,无论它们是否可枚举。 (不包括用字符串命名的。)我们需要一个不同的设置来尝试这个,因为我们在主设置中没有包含 Symbol -named属性。由于忽略了继承的属性,我们将保持简单:

Object.getOwnPropertySymbols returns an array of an object's own properties named with Symbols, regardless of whether they're enumerable. (Leaves out ones named with strings.) We'll need a different setup to try this, since we didn't include Symbol-named properties in our main setup. Since inherited properties are ignored, we'll keep it simple:

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 未列出,因为它有一个字符串名称,而不是符号名称。 符号(x)是第一个,因为它是先创建的。

z wasn't listed because it has a string name, not a Symbol name. Symbol(x) was first because it was created first.

符号比此处所示,但正如您所看到的,如果我们给一个符号命名,我们可以使用 toString 来取回符号(此处的名称)作为字符串。有趣的是,我们必须调用 toString 显式 thesymbol.toString())或使用字符串(theSymbol );将符号附加到字符串时, + 运算符不会为我们执行此操作。

There's a lot more to Symbols than shown here, but as you can see, if we give a symbol a name, we can use toString to get back Symbol(the name here) as a string. Interesting, we have to call toString explicitly (thesymbol.toString()) or use String(theSymbol); the + operator will not do it for us when appending a symbol to a string.

Reflect.ownKeys 返回一个数组对象自己的属性,无论它们如何命名( Symbol 或字符串),以及它们是否可枚举。它忽略了继承的属性:

Reflect.ownKeys returns an array of an object's own properties no matter how they're named (Symbol or string), and whether they're enumerable or not. It ignores inherited properties:

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

注意订单,由 [[OwnPropertyKeys]] 定义: 1 是第一个,因为它是一个字符串这符合整数索引。接下来是 z ,因为它是一个以字符串命名的属性。然后我们按照它们的创建顺序获得 Symbol -named属性。

Note the order, which is defined by [[OwnPropertyKeys]]: 1 was first because it's a string that qualifies as an integer index. z was next because it's a string-named property. Then we have the Symbol-named properties, in the order in which they were created.

这篇关于Javascript:检索对象属性名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆