57

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? 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?
Ian
  • 50,146
  • 13
  • 101
  • 111
Anshul
  • 9,312
  • 11
  • 57
  • 74
  • 1
    I think the main benefit is to make `for in` loops safe - the property won't show up when iterating over the object. Maybe I'm forgetting what enumerability does... – Ian Feb 20 '13 at 16:12
  • 2
    Related: [What are the benefits and dangers of adding methods to Object.prototype in Javascript?](http://stackoverflow.com/questions/3832617/what-are-the-benefits-and-dangers-of-adding-methods-to-object-prototype-in-javas?rq=1). – Felix Kling Feb 20 '13 at 16:18

4 Answers4

45

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().

MDN explains it well with 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. But that makes the property enumerable and messes up what is returned in loops/keys collection (without using .hasOwnProperty...which not everyone uses).

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

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. Another example is the length property, which is not enumerated over.

Here's an example in general: 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)." - from MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys

Ian
  • 50,146
  • 13
  • 101
  • 111
14

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. The user fires up the Node interpreter and creates a new instance of it by calling the constructor:

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. 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] }
Gary Chang
  • 1,042
  • 12
  • 18
  • 9
    non-enumerable properties are still public and still pollute the namespace. If you want private properties, you need a closure. – Matthew Jan 26 '16 at 12:57
  • 5
    I upvoted both the answer and the critical comment. The comment's criticism seems to me to be valid, so perhaps the answerer's use of "public" and "private" may not be optimal. However, I think the point of the answer is still helpful (at least it was to me) because it shows how enumerability can help the library user more easily see what the library author intended for the library user to easily use (e.g. please use `grow` and not `_grow`, even if both are still technically public). – Andrew Willems Mar 07 '16 at 23:44
0
  • 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.
  • 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);
    y.c = 3;
    for(p in y) console.log(p); // this loop will print c, a and b but not toString
    
daniatic
  • 502
  • 4
  • 18
  • 3
    I believe the third point is wrong. Inherited properties are certainly as enumerable as any others... `var x = {a: 1}; var y = Object.create(x); y.b = 2; for (name in y) {console.log(name, y[name]);}` yields `b 2` and `a 1` . – Scott Sauyet Feb 20 '13 at 17:02
  • 1
    I fixed my answer. Excuse my mistake! – daniatic Jan 20 '20 at 18:36
0

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. That's not the case with 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(). If that's your case, you're having to support very old code (i.e., 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 definePropertys just for that. 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. 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.

Lucio Paiva
  • 19,015
  • 11
  • 82
  • 104