Classes: Enumerability of methods and constructor

# Erik Arvidsson (13 years ago)
  1. Are methods supposed to be enumerable?

The native classes (Date etc) don't have enumerable methods and the new method property shorthand for object literals is not enumerable either.

  1. Should constructor be enumerable?

function C() {} var c = new C; c.proto.propertyIsEnumerable('constructor') // false

# Allen Wirfs-Brock (13 years ago)

not enumerable, all around. In the proposal it say class def method will follow the same attribute conventions as obj lit concise methods.

# John J Barton (13 years ago)

On Mon, Mar 26, 2012 at 8:22 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

not enumerable, all around. In the proposal it say class def method will follow the same attribute conventions as obj lit concise methods.

I appreciate understanding why this choice.

(FWIW, I totally don't get why non-enumerable even exists).

jjb

# Brendan Eich (13 years ago)

It's your point about no data properties in the vtable, or the flip side of it. The for-in loop was meant to enumerate built-ins' expando and (in a few cases, ES3 made this more random) data-but-not-function-valued properties, not methods.

It is indeed a just-so story, but the idea is that built-ins have intersting data properties (predefined and possibly user-defined) but the methods are not "interesting" to for-in users.

# Andrea Giammarchi (13 years ago)

never got the problem since ES5 ...

function C() { this.otherEnumerableProp = 123; Object.defineProperty(this, "notEnumerable", {value: 456}); } Object.defineProperties(C.prototype, { method: {value: function method() { // your cool stuff here, not enumerable // writable and configurable through the instance // unless addressed during initialization }}, property: {enumerable: 1, value: null} });

But I would expect non-enumerable in ES6 class definition by default, as it is already for defineProperty/ies and native constructors.

One thing does not scale well in current prototype logic, what has been flagged in the proto is not inherited with same descriptor per each instance ( arguable if expected or not )

my 2 cents

# Brendan Eich (13 years ago)

Andrea Giammarchi wrote:

One thing does not scale well in current prototype logic, what has been flagged in the proto is not inherited with same descriptor per each instance ( arguable if expected or not )

You mean when the prototype property is shadowed in the instance by a property of the same name created by assignment?

That's true and it goes back to the dawn of time. It's not going to change, but as you note, ES5 provides tools other than assignment to create properties with non-default attributes.

# Andrea Giammarchi (13 years ago)

Yep, I mean exactly that ... technically inconsistent due getters/setters ... e.g.

function C() { this.willThrow = "whatever"; } Object.defineProperty(C.prototype, "willThrow", { set: function () { throw "doh!"; } });

so the proto there works as expected unless we don't use defineGetter directly with the new instance with same "willThrow" property name but if rather than set there was a value in the descriptor, nothing will happen and the property is set as configurable,enumerable,writable no matter what was the inherited proto.whatever descriptor.

Also with this mechanism and without proxies proto cannot be used to create frozen instances directly ( or not extendible or sealed )

Not that I need it, just some thought I had on it.

br

# Axel Rauschmayer (13 years ago)

It is indeed a just-so story, but the idea is that built-ins have intersting data properties (predefined and possibly user-defined) but the methods are not "interesting" to for-in users.

My impression has always been that enumerability was mainly introduced to “fix” for-in [1] – it only affects for-in (apart from Object.keys). And it hides the “length” data property in array instances, so that for-in only iterates over array indices (obviously, unless one adds more enumerable properties).

I’ve always found it difficult to formulate a good rule of thumb for enumerability, apart from “ensures that for-in works”. Object.keys() also reacts to enumerability, but in a way that has little to do with for-in (given that it only operates on own properties). Furthermore, I find myself frequently using Object.getOwnPropertyNames, e.g. to find out what methods a constructor’s prototype has. And that method should be the one that is much less frequently used, according to the ES naming conventions.

So, the current narrative for enumerability seems to me a bit disjointed and mostly about for-in. Given that for-in is on its way out and given that inherited data properties are rarely a good idea, would it make sense to change that narrative?

[1] www.2ality.com/2011/07/js-properties.html

# Russell Leggett (13 years ago)

On Tue, Mar 27, 2012 at 7:22 AM, Brendan Eich <brendan at mozilla.com> wrote:

Andrea Giammarchi wrote:

One thing does not scale well in current prototype logic, what has been flagged in the proto is not inherited with same descriptor per each instance ( arguable if expected or not )

You mean when the prototype property is shadowed in the instance by a property of the same name created by assignment?

That's true and it goes back to the dawn of time. It's not going to change, but as you note, ES5 provides tools other than assignment to create properties with non-default attributes.

At least if you stick with the class syntax and extension to overwrite parent properties, it will remain consistent.

# John J Barton (13 years ago)

On Mon, Mar 26, 2012 at 10:38 PM, Brendan Eich <brendan at mozilla.com> wrote:

It's your point about no data properties in the vtable, or the flip side of it. The for-in loop was meant to enumerate built-ins' expando and (in a few cases, ES3 made this more random) data-but-not-function-valued properties, not methods.

It is indeed a just-so story, but the idea is that built-ins have intersting data properties (predefined and possibly user-defined) but the methods are not "interesting" to for-in users.

However, if we now look at the role of for-in in JS code, we have to conclude that its behavior on built-ins is a bug. for-in enumerates 'all' properties of objects; for-in/keys() are critical parts of JS reflection. Any exceptions (eg proto) should be exceptional. That behavior should extend to classes (and builtins, enumerators of expandos can easily skip functions).

Excluding data properties from [[Prototype]]

  1. avoids a serious, difficult to debug data-sharing foot gun problem,
  2. has several simple alternatives. These are the same criteria we should apply to non-enumeration.

Preventing for-in or Object.keys enumeration of classes avoids what? Has which alternatives?

We do have a problem among for-in/keys() and enumeration: we do not have good solutions to separately enumerate functions (methods) and data. That is the problem. Solving this problem be willy-nilly non-enumeration of methods is not a good path. Ok maybe willy-nilly is a bit strong ;-)

jjb

# Brendan Eich (13 years ago)

John J Barton wrote:

We do have a problem among for-in/keys() and enumeration: we do not have good solutions to separately enumerate functions (methods) and data. That is the problem. Solving this problem be willy-nilly non-enumeration of methods is not a good path. Ok maybe willy-nilly is a bit strong ;-)

Yes, too strong. JS is already specified in detail regarding non-enumerable methods on build-on constructors' prototype properties (and for some other properties).

If classes and method definition syntax are meant to model built-ins and their proto-methods, or even if the same use-cases motivate making methods non-enumerable in both cases, then we should not start making methods enumerable.

If on the other hand classes (and arguably method definition syntax) are sugar for the user-defined prototypal pattern, pre-ES5, then all the prototype methods set by assignment will indeed be enumerable.

So again, which is the target use-case? Emulating built-ins or sugaring the user-defined prototypal pattern?

Put this way, I agree with you (and with Dave on the earlier point about C.prototype.constructor being configurable): we should pave the cowpath. Anyone wanting non-enumerability (or in the other case, non-configurability) can use ES5 meta-programing APIs.