简体   繁体   English

使属性不可枚举有什么好处?

[英]What are the benefits of making properties non-enumerable?

Enumerability is one of the three attributes of a property: writability, enumerability, and configurability.可枚举性是属性的三个属性之一:可写性、可枚举性和可配置性。 My questions are:我的问题是:

  • What are the benefit of making properties non-enumerable in JavaScript?在 JavaScript 中使属性不可枚举有什么好处? I know we are hiding the property by making them non-enumerable, but what are the benefit of property hiding?我知道我们通过使它们不可枚举来隐藏属性,但是隐藏属性有什么好处?
  • Can we access non-enumerable properties?我们可以访问不可枚举的属性吗? If yes, then what is the benefit of making them non-enumerable?如果是,那么使它们不可枚举有什么好处?
  • Are all predefined properties of Objects set as non-enumerable?对象的所有预定义属性是否都设置为不可枚举? Such as the case of Array's pop and push properties being non-enumerable?例如 Array 的poppush属性不可枚举的情况?

I think the main benefit is to be able to control what shows up when enumerating an object's properties, such as for in or Object.keys() .我认为主要的好处是能够控制枚举对象属性时显示的内容,例如for inObject.keys()

MDN explains it well with Object.defineProperty : https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty MDN 用Object.defineProperty很好地解释了它: https : //developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty

So normally, when people want to add a method to Object , such as a polyfill for some method not supported in old browsers, they modify the .prototype .所以通常,当人们想要向Object添加一个方法时,例如为某些旧浏览器不支持的方法添加.prototype ,他们会修改.prototype But that makes the property enumerable and messes up what is returned in loops/keys collection ( without using .hasOwnProperty ...which not everyone uses).但这使得属性可枚举并弄乱了循环/键集合中返回的内容(不使用.hasOwnProperty ...不是每个人都使用)。

So instead of something like:所以而不是像这样的东西:

Object.prototype.myMethod = function () {
    alert("Ahh");
};

you could use Object.defineProperty to explicitly say to have it not be enumerable:您可以使用Object.defineProperty来明确表示它不可枚举:

Object.defineProperty(Object.prototype, 'myMethod', {
    value: function () {
        alert("Ahh");
    },
    enumerable: false
});

That way, for example when you use for (var key in obj) , "myMethod" won't be an item enumerated, and you won't have to worry about using .hasOwnProperty .这样,例如,当您使用for (var key in obj) ,“myMethod”将不会是一个枚举项,并且您不必担心使用.hasOwnProperty The main problem with this is that some browsers don't support it of course: http://kangax.github.com/es5-compat-table/ and that not all libraries/code use it, so you can't always rely on external libraries/code to do use correctly and all the time.这样做的主要问题是一些浏览器当然不支持它: http : //kangax.github.com/es5-compat-table/并且不是所有的库/代码都使用它,所以你不能总是依赖在外部库/代码上始终正确使用。

You can access a non-enumerable property at any time you want, it just won't show up when enumerating the object's properties - that's the main point.您可以随时访问不可枚举的属性,它只是在枚举对象的属性时不会显示 - 这是重点。

And I do believe that all "predefined" properties of objects are non-enumerable.而且我确实相信对象的所有“预定义”属性都是不可枚举的。 By that, I really only mean native properties, not necessarily inherited or created.那个,我真的只是指原生属性,不一定是继承或创建的。 So with your example, pop and push will not be enumerated over, but Array.prototype.indexOf will be if it is created as a polyfill on an old browser that doesn't support that method...which of course, can be avoided by using Object.defineProperty like my example above.因此,对于您的示例,将不会枚举poppush ,但是如果在不支持该方法的旧浏览器上将其创建为 polyfill,则Array.prototype.indexOf将是……这当然可以避免通过使用Object.defineProperty就像我上面的例子一样。 Another example is the length property, which is not enumerated over.另一个例子是length属性,它没有被枚举。

Here's an example in general: http://jsfiddle.net/aHJ3g/这里有一个一般的例子: http : //jsfiddle.net/aHJ3g/

The use and definition of Object.keys is important: "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)." Object.keys的使用和定义很重要:“返回给定对象自己的可枚举属性的数组,其顺序与for-in循环提供的顺序相同(区别在于for-in循环在原型链也是如此)。” - from MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys - 来自 MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys

Another major benefit as I see it, is that it prevents private properties of an object from polluting the public namespace.在我看来,另一个主要好处是它可以防止对象的私有属性污染公共命名空间。

Say you have created and published a powerful library called Cosmos .假设您创建并发布了一个名为Cosmos的强大库。 The user fires up the Node interpreter and creates a new instance of it by calling the constructor:用户启动 Node 解释器并通过调用构造函数创建它的新实例:

var Cosmos = require('Cosmos');
var cosmos = new Cosmos('my empire');

Now the user simply types cosmos and presses enter to see what public API it supports.现在,用户只需键入cosmos并按 Enter 键即可查看它支持哪些公共 API。 Which of the two do you want the user to see?您希望用户看到这两个中的哪一个?

{ name: 'my empire',
  grow: [Function: grow],
  addStar: [Function: addStar],
  beautify: [Function: beautify],
  implode: [Function: implode],
  destroy: [Function: destroy] }

OR要么

{ _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] }

There are no real practical uses for non-enumerable properties不可枚举的属性没有真正的实际用途

This is a very nice question that I was about to ask myself.这是一个非常好的问题,我正要问自己。 After a bit of investigation, my conclusion is: you most certainly don't need this feature.经过一番调查,我的结论是:您肯定不需要此功能。

Background背景

For those who don't know what a non-enumerable property is, take a look at the links below:对于那些不知道什么是不可枚举属性的人,请查看以下链接:

You can actually enumerate them你实际上可以枚举它们

This is weird, but enumerating said "non-enumerable" properties is actually very simple:这很奇怪,但是枚举所说的“不可枚举”属性实际上非常简单:

 // 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

When they say you can't enumerate them, they are referring specifically to Object.keys() and the for..in loop and the fact that they only return enumerable properties.当他们说你不能枚举它们时,他们特指的是Object.keys()for..in循环以及它们只返回可枚举属性的事实。 That's not the case with getOwnPropertyNames() . getOwnPropertyNames()不是这种情况。

Don't use it不要使用它

Ok, now that we are on the same page: it looks to me like an obscure language feature, one that would only make my code more difficult to read and understand.好的,现在我们在同一页面上:在我看来,它就像一种晦涩的语言功能,只会使我的代码更难以阅读和理解。 I simply can't think of a truly legitimate use for it.我简直想不出真正合法的用途。

Both existing answers talk about two very specific cases:现有的两个答案都讨论了两个非常具体的案例:

  1. it is useful when you are having to mess with a prototype of some third-party object to add some polyfill method without breaking existing code in the specific case that the code doesn't protect itself using hasOwnProperty() or Object.keys() .当您不得不弄乱某些第三方对象的原型以添加一些 polyfill 方法而不破坏现有代码时,在代码不使用hasOwnProperty()Object.keys()保护自己的特定情况下,它很有用。 If that's your case, you're having to support very old code (ie, legacy code that doesn't comply with today's best practices) and I feel sorry for you (although I know there are a lot of systems still being maintained today which fall into that case, unfortunately);如果这是您的情况,您将不得不支持非常旧的代码(即不符合当今最佳实践的遗留代码),我为您感到抱歉(尽管我知道今天仍有很多系统仍在维护中不幸的是,陷入这种情况);

  2. it is useful for when you're developing a public library and want to keep your public-facing object clean.当您开发公共图书馆并希望保持面向公众的对象干净时,它很有用。 That is very specific, right?那很具体吧? And I also would prefer not to pollute my library's code with several defineProperty s just for that.而且我也不想用几个defineProperty来污染我图书馆的代码。 Moreover, that answer is quickly getting old because we now have private fields .此外,这个答案很快就会过时,因为我们现在有私有字段 Finally, the answer also states that it would also keep the public namespace clean, but that's not true;最后,答案还指出它还会保持公共命名空间的清洁,但事实并非如此; the namespace is being polluted by the object itself, not its properties.命名空间被对象本身污染,而不是它的属性。

So, unless you're in unfortunate position of having to maintain old code, forget about using this.因此,除非您不幸不得不维护旧代码,否则请不要使用它。 You don't need it, trust me.你不需要它,相信我。 If you find yourself in the situation that you need to hide some properties when enumerating an object, you are certainly modeling it wrong.如果您发现自己在枚举对象时需要隐藏某些属性,那么您肯定是建模错误。 Just put those properties in a separate object.只需将这些属性放在一个单独的对象中。 They are not meant to live together with the other properties you have.它们并不意味着与您拥有的其他房产一起生活。 This will keep you code cleaner and easier to understand, which is the single most important thing you should strive to achieve when writing code.这将使您的代码更清晰、更易于理解,这是您在编写代码时应该努力实现的最重要的事情。

Here's a more detailed article corroborating the idea that non-enumerable properties play no significant role nowadays.这是一篇更详细的文章,证实了不可枚举属性在当今没有重要作用的观点。

  • By making the property non-enumerable you can still access it.通过使属性不可枚举,您仍然可以访问它。 But when you apply a for in loop on the object the non-enumerable property won't be iterated.但是当您在对象上应用 for in 循环时,不会迭代不可枚举的属性。
  • See first point见第一点
  • Inherited properties are enumerable (as long as they are marked enumerable)继承的属性是可枚举的(只要它们被标记为可枚举)

     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); yc = 3; for(p in y) console.log(p); // this loop will print c, a and b but not toString

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM