可枚举性是属性的三个属性之一:可写性、可枚举性和可配置性。我的问题是:
- 在 JavaScript 中使属性不可枚举有什么好处?我知道我们通过使它们不可枚举来隐藏属性,但是隐藏属性有什么好处?
- 我们可以访问不可枚举的属性吗?如果是,那么使它们不可枚举有什么好处?
- 对象的所有预定义属性是否都设置为不可枚举?例如数组
pop
和push
属性不可枚举的情况?
可枚举性是属性的三个属性之一:可写性、可枚举性和可配置性。我的问题是:
pop
和push
属性不可枚举的情况?我认为主要的好处是能够控制在枚举对象的属性时显示的内容,例如for in
or Object.keys()
。
MDN 很好地解释了它Object.defineProperty
:https ://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty
所以通常,当人们想要Object
为.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
如果它是在不支持该方法的旧浏览器上创建为 polyfill...当然,可以通过使用Object.defineProperty
我上面的示例来避免。另一个例子是length
属性,这里就不一一列举了。
这是一个一般的例子:http: //jsfiddle.net/aHJ3g/
的使用和定义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
并按下回车键即可查看它支持的公共 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] }
这是一个非常好的问题,我正要问自己。经过一番调查,我的结论是:你肯定不需要这个功能。
对于那些不知道什么是不可枚举属性的人,请查看以下链接:
这很奇怪,但枚举所说的“不可枚举”属性实际上非常简单:
// 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()
。
好的,现在我们在同一页上:在我看来,它就像一种晦涩难懂的语言功能,只会让我的代码更难阅读和理解。我根本想不出真正合法的用途。
现有的两个答案都讨论了两个非常具体的案例:
当您不得不弄乱某些第三方对象的原型以添加一些 polyfill 方法而不破坏现有代码的特定情况下代码不使用hasOwnProperty()
or保护自己时,它很有用Object.keys()
。如果这是您的情况,您将不得不支持非常旧的代码(即不符合当今最佳实践的遗留代码),我为您感到抱歉(尽管我知道今天仍有很多系统仍在维护不幸的是,属于这种情况);
当您开发公共库并希望保持面向公众的对象干净时,它很有用。这是非常具体的,对吧?而且我也不想defineProperty
仅仅为此而用几个 s 污染我的图书馆的代码。此外,这个答案很快就会过时,因为我们现在有私有字段。最后,答案还表明它也会保持公共命名空间的清洁,但事实并非如此;命名空间被对象本身污染,而不是其属性。
因此,除非您不幸不得不维护旧代码,否则请忘记使用它。你不需要它,相信我。如果您发现自己在枚举对象时需要隐藏某些属性,那么您肯定是在建模错误。只需将这些属性放在单独的对象中即可。它们并不意味着与您拥有的其他财产一起生活。这将使您的代码更清晰,更易于理解,这是您在编写代码时应该努力实现的最重要的事情。
这是一篇更详细的文章,证实了不可枚举的属性如今已不起重要作用。
继承的属性是可枚举的(只要它们被标记为可枚举的)
var x = {a:1, b:2} // a and b are enumerable properties by default
x.propertyIsEnumerable("toString") // returns false, because it is not marked as enumerable
var y = Object.create(x);
y.c = 3;
for(p in y) console.log(p); // this loop will print c, a and b but not toString