枚举性是属性的三个属性之一:可写性、枚举性和配置性。我的问题是:
- 在JavaScript中使属性不可枚举的好处是什么?我知道我们通过使它们不可枚举来隐藏属性,但属性隐藏的好处是什么?
- 我们能访问不可枚举的属性吗?如果可以,那么使它们不可枚举的好处是什么?
- 所有对象的预定义属性是否都设置为不可枚举?例如Array的
pop
和push
属性是否为不可枚举?
枚举性是属性的三个属性之一:可写性、枚举性和配置性。我的问题是:
pop
和push
属性是否为不可枚举?我认为主要好处在于能够控制枚举对象属性时显示的内容,例如for in
或Object.keys()
。
MDN通过Object.defineProperty
进行了很好的解释:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty
因此,通常情况下,当人们想要向Object
添加一个方法(例如某些旧浏览器不支持的polyfill)时,他们会修改.prototype
。但是,这会使属性可枚举,并且会混淆在循环/键集合中返回的内容(而不使用.hasOwnProperty
...并非每个人都使用它)。
所以,与其像这样做:
Object.prototype.myMethod = function () {
alert("Ahh");
};
您可以使用 Object.defineProperty
明确指定它不可枚举:
Object.defineProperty(Object.prototype, 'myMethod', {
value: function () {
alert("Ahh");
},
enumerable: false
});
例如,当您使用for (var key in obj)
时,“myMethod”不会被枚举,并且您无需担心使用.hasOwnProperty
。主要问题是某些浏览器当然不支持它:http://kangax.github.com/es5-compat-table/,并且并非所有库/代码都使用它,因此您不能始终依赖外部库/代码以正确和始终正确地使用它。pop
和push
将不会被枚举,但是如果将Array.prototype.indexOf
作为旧浏览器上的填充使用,则会被枚举...当然,可以通过像上面的示例那样使用Object.defineProperty
来避免。另一个例子是不会枚举的length
属性。Object.keys
很重要:“返回给定对象的自己可枚举的属性数组,顺序与由for-in
循环提供的顺序相同(不同之处在于for-in
循环也会枚举原型链中的属性)。”-来自MDN- https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys。我认为另一个重要的好处是,它可以防止对象的私有属性污染公共命名空间。
比如说你创建并发布了一个名为 Cosmos
的强大库。用户启动 Node 解释器并通过调用构造函数创建了它的新实例:
var Cosmos = require('Cosmos');
var cosmos = new Cosmos('my empire');
现在用户只需键入cosmos
并按Enter键即可查看它支持的公共API。你想让用户看到这两个中的哪一个?
{ name: 'my empire',
grow: [Function: grow],
addStar: [Function: addStar],
beautify: [Function: beautify],
implode: [Function: implode],
destroy: [Function: destroy] }
或者
{ _age: 25000,
_size: 35000,
_destroyed: false,
name: 'my empire',
_numStars: 200,
_init: [Function: _init],
grow: [Function: grow],
_grow: [Function: _grow],
addStar: [Function: addStar],
_checkStatus: [Function: _checkStatus],
beautify: [Function: beautify],
implode: [Function: implode],
destroy: [Function: destroy] }
grow
而不是_grow
,即使两者都从技术上来说是公共的)。 - Andrew Willems这是一个非常好的问题,我本来也想问自己。经过一番调查,我的结论是:你绝对不需要这个功能。
对于那些不知道什么是非可枚举属性的人,请看下面的链接:
这很奇怪,但是枚举所谓的“非可枚举”属性实际上非常简单:
// start with some enumerable properties
const foo = {
a: 1,
b: "yes",
c: function () {}
}
// then add a couple of non-enumerable ones
Object.defineProperty(foo, "d", { value: "hiding here", isEnumerable: false });
Object.defineProperty(foo, "e", { value: 42, isEnumerable: false });
const enumerableProperties = Object.keys(foo).join(", ");
console.info("Enumerables: " + enumerableProperties);
// Enumerables: a, b, c
const ownPropertyNames = Object.getOwnPropertyNames(foo).join(", ");
console.info("Enumerating also the non-enumerables: " + ownPropertyNames);
// Enumerating also the non-enumerables: a, b, c, d, e
Object.keys()
和for..in
循环以及它们只返回可枚举属性。而对于getOwnPropertyNames()
来说并非如此。
好的,既然我们在同一个页面上:在我看来,这似乎是一种晦涩的语言特性,只会使我的代码更难阅读和理解。我简直想不出真正合理的用途。
现有的答案都谈到了两个非常具体的情况:
继承的属性是可枚举的(只要它们被标记为可枚举)
var x = {a:1, b:2} // a和b默认情况下是可枚举属性
x.propertyIsEnumerable("toString") // 返回false,因为它没有标记为可枚举
var y = Object.create(x);
y.c = 3;
for(p in y) console.log(p); // 这个循环将打印c、a和b,但不包括toString
var x = {a: 1}; var y = Object.create(x); y.b = 2; for (name in y) {console.log(name, y[name]);}
,将输出 b 2
和 a 1
。 - Scott Sauyet
for in
循环更加安全 - 在迭代对象时,该属性不会显示出来。也许我忘记了枚举的作用... - Ian