在查看我们的选项之前,请快速记下有关javascript中属性的四个关键内容:
-
对象可以有自己的属性,以及从原型对象继承的属性。
-
属性可以是
可枚举的
或
不可枚举的
.
-
属性的名称可以是
串
或(截至ES2015/ES6)以下名称:
Symbol
S.
-
属性的名称不能是数字,如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]公司
规范中的操作,即订单为:
所以我们得到
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的字符串。然后我们有了
符号
-命名属性,按创建顺序排列。