classes and enumerability

# Andrea Giammarchi (8 years ago)

Dear Santa, I wish since ES3 era that properties defined on a prototype are by default not enumerable.

By any chance we can make this happen in ES6 classes?

Or better ... why would anyone expect or want them to be enumerable?

To define a class is a very explicit intent, I believe having those definitions non enumerable would be a preferred option for majority of developers that have been doomed in for/in since kinda ever before enumerable was not configurable.

All default methods and properties in native prototypes are not enumerable, why does it have to be so inconsistent with userland?

You also know ES6 is the only window we have 'cause after that it will be a breaking change.

Thank you at least for any sort of extra/concrete clarification about this choice.

Merry Christmas

# Boris Zbarsky (8 years ago)

On 12/23/14 4:35 PM, Andrea Giammarchi wrote:

Or better ... why would anyone expect or want them to be enumerable?

This is a good question. Note that there is web content that depends on the current setup wherein stuff on DOM prototypes is enumerable... you may want to check on why exactly they do that.

I agree that it would have been better all around if none of this stuff had been enumerable historically. As things stand, ES builtins are non-enumerable, DOM builtins are enumerable, and someone designing an API and wanting to be "consistent with the platform" is screwed.

The situation is especially bad for things that start off in the DOM and then move into ES; for example right now Gecko and Blink are not interoperable on whether "then" is an enumerable property of Promise.prototype...

You also know ES6 is the only window we have 'cause after that it will be a breaking change.

Pretty much anything here is a breaking change, sadly, no matter what we pick. Certainly your proposed change is a breaking one if the intent is that Web IDL outputs ES classes...

# Rick Waldron (8 years ago)

On Tue Dec 23 2014 at 7:35:44 PM Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

Dear Santa, I wish since ES3 era that properties defined on a prototype are by default not enumerable.

By any chance we can make this happen in ES6 classes?

ES6 is done, properties of user created prototypes are enumerable by default. They can be made non-enumerable if you want them that way, but doing so by default is inconsistent with constructor function + prototype definition.

Or better ... why would anyone expect or want them to be enumerable?

  • Consistency with constructor function + prototype definition (severe refactoring hazard).

  • Pretty useful for dynamically creating collection APIs, eg:

    Object.keys(Servo.prototype).forEach(function(method) { // Create Servos collection class wrappers for each method Servos.prototype[method] = function() { var args = [].slice.call(arguments); this.each(function(servo) { Servo.prototype[method].apply(servo, args); }); return this; }; });

To define a class is a very explicit intent, I believe having those definitions non enumerable would be a preferred option for majority of developers that have been doomed in for/in since kinda ever before enumerable was not configurable.

All default methods and properties in native prototypes are not enumerable, why does it have to be so inconsistent with userland?

The widely understood, wholly well-known semantics:

  • Properties of built-in prototype objects are non-enumerable
  • Properties of user defined prototype objects are enumerable by default, but can be made non-enumerable if desired.

You also know ES6 is the only window we have 'cause after that it will be a breaking change.

No: any future annotation syntax must allow for defining writable, configurable and enumerable attributes.

Thank you at least for any sort of extra/concrete clarification about this choice.

rwaldron/tc39-notes/blob/master/es6/2012-09/sept-18.md#concise-method-definition-revisited

rwaldron/tc39-notes/blob/c61f48cea5f2339a1ec65ca89827c8cff170779b/es6/2014-01/jan-28.md#concise-methods-and-enumerability

The consistency argument has won every time this conversation is brought up. There are ways to get what you want in the future (probably even in ES7), but changing the default is not the solution.

# Glen Huang (8 years ago)

There was some heated debate on twitter too just yesterday: twitter.com/domenic/status/547146484479561730, twitter.com/domenic/status/547146484479561730

Just wondering, is es6 class frozen? In those tweets Brendan proposed "enum in obj lit, not in class”. Looks like class is still open for modification?

# Brendan Eich (8 years ago)

It ain't over till it's over. If we can't tweak ES6 to fix a mistake, just because process, then we're doing it wrong. OTOH the bar for any change, including what is regarded by many (but not all) as a fix, is very high.

We should take advantage of es-discuss to debate the crucial question, which is not whether enumerable prototype methods are the rule or exception -- as Rick said, for built-ins (but not all DOM methods), non-enumerable is the common case; for user-defined classes prior to ES5 and Object.defineProperty, enumerable is the norm.

This is true in the rule/exceptions/confusion sense we all know and hate.

The question is: what should ES6 classes choose as the default? What's the most useful default, independent of various backward-looking consistencies? What, if the future is bigger than the past, would be best?

In the twitter thread, @wycats and I take on the idea that consistency with object literals is required. I hope that's not controversial. Class bodies are not object literals, in spite of sharing some sub-notation (method shorthanding, mostly).

# Gary Guo (8 years ago)

I would say enumerate through classes' or instances' properties are a strange use and should be discouraged. I would prefer nothing but instance properties to be enumerable. To me, it is illogical for methods to be enumerable.

# Jeremy Martin (8 years ago)

In all the discussions I've observed, the primary argument for enumerability has been consistency with ES5 prototype conventions. I don't think I've seen a single argument in favor, though, based on the actual intrinsic merits of that behaviour.

So, do such arguments exist?

# Brendan Eich (8 years ago)

Jeremy Martin wrote:

In all the discussions I've observed, the primary argument for enumerability has been consistency with ES5 prototype conventions. I don't think I've seen a single argument in favor, though, based on the actual intrinsic merits of that behaviour.

Right, hence my tweet about "A foolish consistency". And the other tweet against the argument-from-it's-always-been-that-way(-in-ES3-level-JS) fallacy.

Even if you grant that enumerability is unreformable, we have to pick a "better consistency".

It seems to me that DOM quirky enumerability of prototype methods, and legacy ES3-ish enumerability of user-defined methods, do not sum up to "better'.

I tweeted several times that the legacy ES3 and below resulting in enumerability for theprototypal-pattern's class prototype methods is a bug not a feature.

On the DOM quirks point, built-in classes and self-hosting of built-ins trump DOM quirks any day. Boris may have a census that argues against my weighting, though.

Hence my support for a last-minute fix.

# Kevin Smith (8 years ago)

The question is: what should ES6 classes choose as the default? What's the most useful default, independent of various backward-looking consistencies? What, if the future is bigger than the past, would be best?

Framed that way, then non-enumerability.

If we want to preserve any kind of conceptual integrity for enumerability, then enumerability must indicate that the property is a data element within a data structure.

Whether enumerability is worth preserving, I don't know.

# Russell Leggett (8 years ago)
# Kevin Smith (8 years ago)

I'll just throw out mixins as a possible reason to keep enumerability.

I don't think enumerability really bears on mixins in ES6, because (for one) the mixin source could have symbol-named properties. A fully-featured mixin function will need to use Object.getOwnPropertySymbols, etc.

# Glen Huang (8 years ago)

Actually mixins shouldn’t be done with Object.assign if that’s what you mean.

I think the language should provide a syntax for that (e.g., Lightweight traits

# Glen Huang (8 years ago)

Although I guess it’s not very easy to desugar to es5 if there is no way to enumerate prototype methods/properties. (probably impossible?)

# Gary Guo (8 years ago)

Object.defineProperty could easily do that.

# Glen Huang (8 years ago)

How?

Given that you don’t know the names of the prototype properties and methods contained (could be deep in the prototype chain) in a object. How do you add these to another object?

# Glen Huang (8 years ago)

I guess it’s probably a misunderstanding.

I was saying that a feature like lightweight traits is probably impossible to desugar to es5, if class syntax makes properties and methods non-enum.

# Kevin Smith (8 years ago)

Given that you don’t know the names of the prototype properties and methods contained (could be deep in the prototype chain) in a object. How do you add these to another object?

getOwnPropertyNames, getOwnPropertySymbols, and getPrototypeOf to traverse the prototype chain.

# Glen Huang (8 years ago)

Oh, forget about those. Thanks. Then problem solved.

non-enum class syntax + trait syntax. This could make prototypal inheritance a joy to write. :)

# Glen Huang (8 years ago)

Although when desugaring to es5, no need to consider symbol properties.

# Boris Zbarsky (8 years ago)

On 12/23/14 8:49 PM, Brendan Eich wrote:

On the DOM quirks point

Here's the thing. Ignoring for the moment the classes situation, it seems to me that for "DOM" stuff specifically we have three options:

  1. Have all "DOM" prototype stuff be enumerable. This leaves us in a Conway's Law situation where whether stuff is enumerable or not depends on whether the spec is written by the W3C/WhatWG or TC39. That's where we've been for going on two decades now, but it still sucks.

  2. Have some "DOM" stuff (whatever is currently shipping?) enumerable but new stuff not enumerable (basically introduce a [LegacyEnumerableProperties] or whatnot in Web IDL and sprinkle it on everything currently shipping). This still means we have inconsistent behavior, but now it's inconsistent in time as opposed to organizational structure. And it relies on UAs being able to make this change quickly for new APIs they add. Gecko can do that, but I can't speak for others.

  3. Try to make all "DOM" stuff non-enumerable, dealing with compat issues by adding [LegacyEnumerableProperties] in the few cases where they arise. This risks landing us in the same place as #2 depending on how many interfaces need the annotation, plus compat fallout as we discover which interfaces need it. But it would maybe get us to the point where we don't have the Conway's Law problems, and it may well turn out that very few interfaces need the [LegacyEnumerableProperties] thing.

If I were doing green-field implementation of the DOM, of course, option 3 it would be, to match ES builtins....

Apart from all that, we've generally been aiming at converging Web IDL behavior on the behavior of ES6 classes as much as we can, and continue to do so. So if classes end up with stuff non-enumerable by default that would militate in favor of option 3 above. Which is great except for the possible compat fallout. I'm game for trying it, though, if other UAs are.

# Brendan Eich (8 years ago)

ES5 has Object static methods for getting all own property names, enumerable or not. Also walking prototype chain. What's the problem you're thinking of, exactly?

# Brendan Eich (8 years ago)

Somehow the thread forked, and I see Kevin Smith already wrote a reply on the other branch like mine -- sorry about that. But how did the thread fork?

# Glen Huang (8 years ago)

Nothing. I forgot about those methods, and Kevin also kindly reminded me.

Thanks for letting me know. :)

# Andrea Giammarchi (8 years ago)

I've replied to Rick privatly by accident but regardless what I've written there, which is basically agereing with Brendan on the following sentence:

It ain't over till it's over. If we can't tweak ES6 to fix a mistake,

just because process, then we're doing it wrong.

and describe why his example can be done anyway (using getOwnPropertyNames instead of keys) and why object literals shouldn't be affected indeed ... there is a very strong point to me that is the main bummer ...

class List extends Array {
   itsGoingToBeEnumerable() {
     return 'but does it have to and would you expect to?';
   }
}

Not only being fresh new sugar, class could bring more than what basic ES3 prototype assignment has done for 15+ years, forcing historically every developer to be worried about doomed for/in loops and asking randomly any sort of native methods in specs so that would feel safer 'cause not enumerable, given the ability to finally subclass natives will mess up expectations even more: an instanceof Array that could have methods in the loop and cannot be used as sparse array ... this is just an example that will doom again subclassing and loops ... does it have to be like this ?

Subclassing is planned and basically unshimmable ... why should we keep bringing further old gotchas instead of finally cleaning up through a new syntax that is fully compatible with ES5 when it comes to transpiled prototype properties definition ?

Please let's make class developer friendly, giving them the option to fallback to classical prototype literal object definition, when and if needed, providing a better pattern for modern architectures built on top of ES6, 7 and others.

Thanks for consideration.

Best

# Kevin Smith (8 years ago)
class List extends Array {
   itsGoingToBeEnumerable() {
     return 'but does it have to and would you expect to?';
   }
}

That's a good point.

As far as I can tell, the downside risk associated with making class methods non-enumerable centers around mixins:

  • Legacy mixin utilities will tend to fail when given ES6 classes.
  • We have no built-in function for doing proper mixins yet, and Object.assign won't work at all.

I looked through my ES6 code and found a place where I was using Object.assign as an simplistic mixin function. It might be a pain to have to import a userland utility for such cases.

# Brendan Eich (8 years ago)

Kevin Smith wrote:

I looked through my ES6 code and found a place where I was using Object.assign as an simplistic mixin function.

ES5 introspection APIs are enough, and if you're mixing in from an ES6 class using old code, you face the same non-enumerable-methods surface that you would with a built-in class.

It might be a pain to have to import a userland utility for such cases.

How so?

# Andrea Giammarchi (8 years ago)

I don't see any risk around mixins for the following reasons:

  1. mixins are nowhere in specs so any solution is personal/ad-hoc but ...
  2. if you use Object.assign you don't do that at class definition time, you do that eventually after. This possibility will be unchanged, you can still attach whatever you want as enumerable property to the class prototype once defined ... the idea here is not to not make that possible anymore, rather to have a default definition that is configurable but not enumerable (+ writable for methods and values or get/set for get/setters)
  3. using literals to define traits is still the easiest way to go and it also define a clear line between a class and a trait. Once ES7 will have traits syntax within the class one we can assume traits are imported during class definition and, as such, will be assigned non enumerable. This would still be the least surprise in my opinion

Moreover, using Object.assign for mixins is already a very poor pattern 'cause also getters and setters will be lost during the procedure, adding an extra surprise in the middle.

Object.assign is good to extend own properties and copy options and defatuls values, promoting or using it to define the future of traits in JS ... well, I'd personally wouldn't go for it and think about better solutions for ES7

Meanwhile, we should be careful to base the present (ES6) and the future based on what we used to do in ES3, hence a window to improve classses.

Best

# Kevin Smith (8 years ago)

ES5 introspection APIs are enough, and if you're mixing in from an ES6 class using old code, you face the same non-enumerable-methods surface that you would with a built-in class.

True, and even more so, I like Andrea's point that mixing in getters and setters requires ES5 APIs anyway (i.e. Object.getOwnPropertyDescriptor).

It might be a pain to have to import a userland utility for such cases.

How so?

Meaning simply that Object.mixin (or whatever it might be called, or syntax) seems like a standard API gap. But I suppose it's a gap regardless of whether class methods are enumerable.

# Axel Rauschmayer (8 years ago)

The alternative is to treat enumerability the way ES6 treats holes: pretend it doesn’t exist:

  • Use Reflect.ownKeys, Object.getOwnPropertyNames, Object.getOwnPropertySymbols instead of Object.keys.
  • Don’t use the for-in loop (an easy one…)
  • Change Object.assign so that it considers all properties, not just enumerable ones.
  • The properties of class prototypes remain non-enumerable.
  • I’m unsure about new built-in instance prototypes. For consistency’s sake, one may want to make them non-enumerable. But how would they be different from a library?

Quoting Sebastian Markbåge: twitter.com/sebmarkbage/status/547156703104753664

"enumerable" is just one of an infinite number of categories you might want to filter on. It's a hack and should die.

Would this approach have any disadvantages?

# Rick Waldron (8 years ago)

Inline

On Tue Dec 23 2014 at 10:24:06 PM Brendan Eich <brendan at mozilla.org> wrote:

It ain't over till it's over. If we can't tweak ES6 to fix a mistake, just because process, then we're doing it wrong. OTOH the bar for any change, including what is regarded by many (but not all) as a fix, is very high.

We should take advantage of es-discuss to debate the crucial question, which is not whether enumerable prototype methods are the rule or exception -- as Rick said, for built-ins (but not all DOM methods), non-enumerable is the common case; for user-defined classes prior to ES5 and Object.defineProperty, enumerable is the norm.

This is true in the rule/exceptions/confusion sense we all know and hate.

The question is: what should ES6 classes choose as the default? What's the most useful default, independent of various backward-looking consistencies? What, if the future is bigger than the past, would be best?

Put in these terms, I still think that maintaining status quo makes sense. The future is bigger than the past and this can be addressed via annotations (maybe even in ES7?).

It seems worthwhile to mention non-enumerability has its own problems: recall that Array.prototype.contains was recently renamed to Array.prototype.includes exactly because the built-in is non-enumerable by default. It could've been argued that this was a good reason to break with that norm and make built-ins enumerable by default. That of course is a non-starter—so why is breaking "enumerable by default" for user defined classes under consideration?

Non-enumerability can still be achieved, it's not pretty (it downright sucks), but most user code simply doesn't go out of its way to explicitly define properties as non-enumerable:

  • Compiled a list of 600 modules that npm shows as the most depended on ( www.npmjs.com/browse/depended, pages 1-10).
  • I created a script that installed all 600 modules
  • Confirmed installation by attempting to require all of them. Three created issues when require'ing, but otherwise all modules loaded (typescript, fstream-ignore, fis-parser-less)
  • Globbing "node_modules/**/*.js" produced 35371 files, so do a naive filter for paths with "test" in it (assume this just a test file, whether that's correct or not, as we'll see: it doesn't matter). Also only look at a file once by tracking the actual module file path and not the complete path. This brings the number down to 11038 files.
  • Read the source of each file, line by line, counting occurrences of the following:
    • enumerable: false,
    • enumerable:false,
    • 'enumerable': false
    • 'enumerable':false
    • "enumerable": false
    • "enumerable":false

Here are all the occurrences, marked by file and line number: gist.github.com/rwaldron/8989d6e7cd7b2f6bfdbb

Here is the summary: Total Files Read: 11038 Files Containing Explicit 'enumerable: false': 149 Occurrences of 'enumerable: false' (and variants): 206

I was ready to concede until I did this exercise, but I'm holding my position that the default should remain enumerable. A vocal minority shouldn't trump the reality of programmer's expectations.

# Kevin Smith (8 years ago)

Here is the summary: Total Files Read: 11038 Files Containing Explicit 'enumerable: false': 149 Occurrences of 'enumerable: false' (and variants): 206

I love this kind of analysis - thanks!

My interpretation of this data is that non-enumerability isn't important enough to bother with in the vast majority of cases. It may still be that non-enumerability is the least-surprise option for class methods, but certainly users don't care enough about the issue currently to bother with typing in "enumerable: false".

I'm still concerned about the refactoring hazard argument. I think the idea is that if I change some ES5-style class into an ES6-style class, then I need to have good unit tests already in place to make sure that non-enumerability won't break things. Otherwise, I can't do the refactor.

What do we think about that one?

# Rick Waldron (8 years ago)

On Wed Dec 24 2014 at 4:49:36 PM Kevin Smith <zenparsing at gmail.com> wrote:

Here is the summary:

Total Files Read: 11038 Files Containing Explicit 'enumerable: false': 149 Occurrences of 'enumerable: false' (and variants): 206

I love this kind of analysis - thanks!

I was actually inspired by a similar report you created for the list in the past.

My interpretation of this data is that non-enumerability isn't important enough to bother with in the vast majority of cases.

Yes, this was my interpretation as well.

It may still be that non-enumerability is the least-surprise option for class methods, but certainly users don't care enough about the issue currently to bother with typing in "enumerable: false".

I'm still concerned about the refactoring hazard argument. I think the idea is that if I change some ES5-style class into an ES6-style class, then I need to have good unit tests already in place to make sure that non-enumerability won't break things. Otherwise, I can't do the refactor.

Agreed: this has also been my main argument when the subject has come up in the past.

# Allen Wirfs-Brock (8 years ago)

On Dec 24, 2014, at 1:49 PM, Kevin Smith wrote:

Here is the summary: Total Files Read: 11038 Files Containing Explicit 'enumerable: false': 149 Occurrences of 'enumerable: false' (and variants): 206

I love this kind of analysis - thanks!

My interpretation of this data is that non-enumerability isn't important enough to bother with in the vast majority of cases. It may still be that non-enumerability is the least-surprise option for class methods, but certainly users don't care enough about the issue currently to bother with typing in "enumerable: false".

I'm still concerned about the refactoring hazard argument. I think the idea is that if I change some ES5-style class into an ES6-style class, then I need to have good unit tests already in place to make sure that non-enumerability won't break things. Otherwise, I can't do the refactor.

Let's drill deeper into this concern...

There are two parts of dealing with a class-abstraction, the definition the class and consuming instances of the class. Let's look at the enumerability impact on both of those.

Class definition: In an ES5-level class abstraction library, changing the enumerability of methods might have an impact on class creation, if the attraction library does something like method copying using for-in. However, what we are talking about is converting from an ad hoc (and possibly library provided) ES5 class abstraction to using actual ES6 class definition. Class definitions don't depend upon reflection of properties to actually create the class. So, I don't think we have anything to worry about on the class definition side. Enumerability of properties is irrelevant.

Class consumption: It pretty much comes down to whether or not consumers of class instances in ES5 use for-in with or without hasOwnProperty filters to enumerate the properties of class instances. If their ES5 code uses hasOwnProperty to filter out methods, then the fact that ES6 methods are non-enumerable will make no difference. On the other hand, if consumers of these ES5 class instances are doing for-in enumerations and want to see inherited methods then there would be an issue. But, other than for meta programming, it hard to imagine why they would actually want tfor-in to see any methods.

Some folks at Auburn University have been studying a large corpus of JavaScript code, see munawarhafiz.com/research/jssurvey On of the things they've look at is use of for-in and hasOwnProperty. Take a look at the data shown on that page for for-in usage and read sections X and XI in the accompany paper.

Personally, I have always believed we are going down the wrong path by switching (from the original max-in class design) to making methods defined within a class definition enumerable.

The only real model of class-like abstractions that has always and universally been a part of JS is the model that the built-in constructors consistently follow. The ES6 class definition model was intended to exactly mirror that built-in model. My vision for it was that future JS programmers should never need to worry about whether a "class" was provided by a built-in or from a library or was a local part of the application, because they all worked the same.

By make class defined methods enumerable, we parted from that simple vision and instead seem to follow the remediation path that current and legacy JS programmers had to follow because the language did not give them the capabilities they need to define classes just like the built-ins. We're tinking too much about the past and not enough about the future.

# Jeremy Martin (8 years ago)

Props to Rick for bringing some analytical rigor into the discussion.

Unfortunately all I can provide in response is more anecdotal and opinion-based, but, nonetheless, here's how I would counter that:

First, people are lazy, and programmers are people. Seeing a lot of non-non-enumerable class methods is, IMO, more indicative of the path of least resistance, not an indicator of calculated, intended behavior.

Second, to quote Kevin from earlier,

If we want to preserve any kind of conceptual integrity for enumerability,

then enumerability must indicate that the property is a data element within a data structure.

A thousand times, this. Properties are state, methods are behavior; this is just OOP 101 (right?). And I don't see any compelling arguments being made for enumerating the behavior of an object.

I hate relying on anecdotes, but for all the times I've used ES5-style "classes", I have always ended up creating enumerable methods simply because that's the default behavior. I have never, ever, done this as a design decision, and I'd wager (since I have the luxury of no data to prove or disprove this) that that's a majority position.

# Brendan Eich (8 years ago)

Rick Waldron wrote:

The question is: what should ES6 classes choose as the default? What's
the most useful default, independent of various backward-looking
consistencies? What, if the future is bigger than the past, would
be best?

Put in these terms, I still think that maintaining status quo makes sense. The future is bigger than the past and this can be addressed via annotations (maybe even in ES7?).

The question I'm raising is not answered by promising annotations, if the right long-term answer is that the annotation-free default should be non-enumerable. This is not something to defer based on hypothesized annotations -- we have to agree (in the general, consensus sense) on the right long-term default.

# Brendan Eich (8 years ago)

Kevin Smith wrote:

Here is the summary:
Total Files Read:  11038
Files Containing Explicit 'enumerable: false':  149
Occurrences of 'enumerable: false' (and variants): 206

I love this kind of analysis - thanks!

Agreed, <3 -- thanks, Rick!

My interpretation of this data is that non-enumerability isn't important enough to bother with in the vast majority of cases.

There's a confounding variable: the pain of ES6s meta-object APIs, in all respects.

It's probably true that enumerability doesn't matter enough for many programmers, but the ES5 MOP is painful enough (both its intrinsic complexity and ES5's indirect costs, e.g., needing to shim into ES3-level engines) that it does not seem prudent to assume only one cause, or to assume which cause is proximate vs. distal.

For those who care about prototype method enumerability, however many they may be, if too few pass the ES5 hurdle (for whatever reason), then the measurements Rick took, while informative, are not decisive.

It may still be that non-enumerability is the least-surprise option for class methods, but certainly users don't care enough about the issue currently to bother with typing in "enumerable: false".

This came up on the twitter thread. If we pick a painful default, people will still probably migrate to the shortest-path-to-joy, arguably class syntax. In this light we can hope not to do too much harm, either way.

But what's best? I'd like to think it's non-enumerability, based on built-ins combined with for-in from the old days. I made built-in methods non-enumerable because that made for-in much more useful, compared to design alternatives (built-in prototype methods enumerable; no such thing as non-enumerable). It may be that even early JS didn't have enough time to experiment and find an even better way, e.g., explicit shallow vs. deep enumeration. That adds more user-facing complexity in choice of looping forms.

I'm still concerned about the refactoring hazard argument. I think the idea is that if I change some ES5-style class into an ES6-style class, then I need to have good unit tests already in place to make sure that non-enumerability won't break things. Otherwise, I can't do the refactor.

What do we think about that one?

Yehuda made the point on twitter that enumerability matters for object literals passed to extend-ish methods, and object literals >> class

prototypes. There are many objects created from literals, some of which are used as arguments to extend. There are few class prototypes, the built-in ones already have non-enumerable methods, and ES6 classes are new enough that if non-enumerable wins on balance, there won't be a refactoring hazard to-do with extends.

Other for-in-based library code than extend could break, but would with a built-in class that otherwise resembled the new ES6-usercode one. So the "design of class obligations" must include choosing and coding some enumerability non-default annotation or extra API boilerplate. Since non-default, it will be less common, possibly rare. So back to the "what's the best long-term default" question we cannot duck right now.

# Brendan Eich (8 years ago)

Brendan Eich wrote:

There's a confounding variable: the pain of ES6s meta-object APIs, in all respects.

I meant "ES5's" here, of course.

Agree with Jeremy, laziness is a programmer virtue and a part of human nature. People are not bothering where they don't know better, or do perhaps know enough but don't have time and cause to take the trouble. The trouble is due to the non-default nature of non-enumerability. Arguing from that past default to (still-future for a little while!) ES6 class's prototype methods being non-enumerable is circular, if you ignore the foolish-consistency argument (which I think we should, but I"m not dismissing here -- just noting that it isn't enough to avoid circularity because of the ES5's-painful-to-use confounder).

# Caitlin Potter (8 years ago)

Supposing that methods were non-enumerable by default, would accessors be different, or also non-enumerable by default?

In most cases, accessors would make sense to be enumerable, e.g. for JSON serialization, but that would be sort of inconsistent and confusing if the default were different from methods

(Just thinking out loud)

# Brendan Eich (8 years ago)

Caitlin Potter wrote:

Supposing that methods were non-enumerable by default, would accessors be different, or also non-enumerable by default?

Non-enumerable, to match built-ins (RegExp.prototype has some notable accessors).

# Andrea Giammarchi (8 years ago)

I'm sorry Rick, that's a great analysis but this is also how I see it, metaphorically speaking:

"most cars in the world use wheels 'cause we don't know better and that's how it always worked"

To be more explicit, here things I would have exepceted anyway:

Find out that developers tried to make things work down to ES3 and IE <= 8 engines

Finding that developers didn't have native way to create classes before so they used known ES3 patterns

Finding that old ES3 based class like architectures never bothered about enumerablility since JSLint does not even accept for/in without a hasOwnProperty check, these same libraries never bothered about getters and setters inheritance too though (most of them)

Find out that Mr. D talked about enumerability as one of the bad part people got used to but new comers will always smash their code against

Find out that someone actually bothered about this, in order to have cleaner intent and expectations, without basing the code over ES3 and understanding and using ES5 at its best ... these probably understand getters and setters too, everyone else should be left out of the equation, IMO

I'd also count how many hasOwnProperty checks are out there in the wild because of all previous historical facts ... and re-read the analysis since, as others said already, are we considering the way the past forced to write things instead of considering the way developers would like things to be?

Back to my first question: why would you ever want a method in a class be enumerable?

Hope you got my point, thanks in any case for providing more data to think about.

Cheers

P.S. this is mostly off topic but I find absolutely hilarious that Custom Elements and document.resgisterElement accept a prototype property that needs an object created through Object.create and a descriptor that for lazyness will make ewverything non enumerable and actually even non configurable and non writable ...


var MyElement = document.registerElement(
  'my-element',
  {
    prototype: Object.create(
      HTMLElement.prototype, {
      myCallback: {value: function() {
      }

going this direction we'll have in the near future custom elements with nothing enumerable, opposite to current Web IDL standard, and JS user classes and subclasses with everything enumerable, opposite to JS native classes ... can I call sheganigans here ? :-)

# Brendan Eich (8 years ago)

Boris Zbarsky wrote:

  1. Try to make all "DOM" stuff non-enumerable, dealing with compat issues by adding [LegacyEnumerableProperties] in the few cases where they arise. This risks landing us in the same place as #2 depending on how many interfaces need the annotation, plus compat fallout as we discover which interfaces need it. But it would maybe get us to the point where we don't have the Conway's Law problems, and it may well turn out that very few interfaces need the [LegacyEnumerableProperties] thing.

We aren't going to reverse-Conway the core language built-ins to have enumerable methods, ever -- so I think the right attack is on the quirky DOM. As usual :-P.

(I'm to blame for some of these quirks, IIRC -- but I don't have Netscape 2 or 3 code around. So, I'm to blame for everything. As usual :-|.)

Are there some DOM prototype methods/accessors that are non-enumerable? Sorry for the dear-lazy-bz mail.

If I were doing green-field implementation of the DOM, of course, option 3 it would be, to match ES builtins....

Yup. This should weigh on us.

Apart from all that, we've generally been aiming at converging Web IDL behavior on the behavior of ES6 classes as much as we can, and continue to do so. So if classes end up with stuff non-enumerable by default that would militate in favor of option 3 above. Which is great except for the possible compat fallout. I'm game for trying it, though, if other UAs are.

Hope to hear from bterlson and someone on chromium/Blink DOM/WebIDL duty.

# Brendan Eich (8 years ago)

Axel Rauschmayer wrote:

The alternative is to treat enumerability the way ES6 treats holes: pretend it doesn’t exist:

That doesn't work, here or for holes. We've actually split APIs with respect to holes, and this will mean bugs. We did make an intentional future-trumps-past choice against holes, though.

Here and on twitter, we seem to have members of the committee on both sides. Not good. Not saying consensus is broken and dissenters from the draft status will throw their bodies in front of the train, but it's worth extended discussion and (thanks again, Rick) measurement of what can be measured.

  • Use Reflect.ownKeys, Object.getOwnPropertyNames, Object.getOwnPropertySymbols instead of Object.keys.
  • Don’t use the for-in loop (an easy one…)
  • Change Object.assign so that it considers all properties, not just enumerable ones.
  • The properties of class prototypes remain non-enumerable.
  • I’m unsure about new built-in instance prototypes. For consistency’s sake, one may want to make them non-enumerable. But how would they be different from a library?

Quoting Sebastian Markbåge: twitter.com/sebmarkbage/status/547156703104753664

"enumerable" is just one of an infinite number of categories you might want to filter on. It's a hack and should die.

Would this approach have any disadvantages?

The prescriptionist approach has not worked. See the Auburn study that Allen cited:

munawarhafiz.com/research/jssurvey/GHB14-JSUsedParts.pdf

People use for-in without hasOwnProperty -- a lot. Some intentionally well-used cases, no doubt -- others accidents waiting to happen.

JS has some bad defaults, requiring long-winded workarounds. Lazy programmers will inevitably skip the workarounds. Let's not do another.

# Andrea Giammarchi (8 years ago)

Out of curiosity, which accessor, being a runtime info that once trapped in a static property looses its meaning, is commonly needed for JSON ?

I am just thinking about Array and the fact that thanks gosh the length is not in. What kind of logic you have in mind?

// to fix the exception
// with ... a **non enumerable** method ;-)
class SomeCase {
  toJSON() {
    var o = Object.assign({}, this);
    o.accessor = this.accessor;
    return o;
  }
}

case solved in few lines and yet another method, the toJSON one, that nobody wants to show up anywhere in an enumerable way.

Best

# Gary Guo (8 years ago)

Actually I believe that there will not be many cases that we need to use for-in loop in ES6. In most cases enumerate through properties in the prototype chain is not preferable. for-of Object.keys should be enough for enumeration with own properties. If my assumption is correct, since we are less likely to use legacy ways, we will be more able to make a change.

# Brendan Eich (8 years ago)

Sorry, you can't use small-world reasoning about programming in the large, especially with JS on the web. for-in proliferated, before hasOwnProperty appeared in ES3. Even after, the burden was high enough, per that study Allen cited (munawarhafiz.com/research/jssurvey/GHB14-JSUsedParts.pdf). for-in ain't going away.

If you are saying we should make class prototype methods non-enumerable in ES6, I agree. If you're saying it won't matter that they remain as drafted, enumerable, because everyone will stop using for-in. I don't buy it. If you are suggesting that for-in-based code won't be run on class prototypes, perhaps -- but that's a gamble.

# Caitlin Potter (8 years ago)

An assessor can produce data which is desired serialization, period. For instance, a setter might be used to enforce rules on valid values, which should still be serialized. A proxy could do this too, but nobody really wants to use proxies for everything.

On the other hand, certain assessor properties you would definitely not want to serialize, to avoid providing sensitive, unnecessary, or circular data to a serializer. So it's hard to pick the most sensible default.

I don't think built in accessors really matter much, because things like Array length becomes useless when serialized, but user defined accessors often aren't useless or undesirable to serialize (at least in data persistence libraries and similar things).

It's hard to produce a cohesive argument or example from a phone, though, I'll get back to this on another day.

# Andrea Giammarchi (8 years ago)

I'm sure there are cases, that's why I've written a possible solution that is quite straight forward for those cases, but I am still curious to see a concerete example. In other PL, when you serialize in order to deserialize in a 1:1 way, you never have getters/setters in, just the properties that will make again those getters/setters meaningful.

However, to just pass data around, I see if that accessor contains a sensible info, of course you want it in.

I wonder if this is really the most common case though ... and if we should make the enumerability desired as such most common case.

We could have the ability to chose instead and use old patterns when needed, through all object literals improvements and over prototype assignment ... but, can we have something better and more suitable for classes wiith the new ES6 class sugar?

Going "all enumerable" is not future friendly, rather past friendly, and the whole idea of ES.next is to have the past still compatible within new syntax. Why carrying old gotchas in is not a good way to clean-up or go toward better patterns.

Cheers

# Andrea Giammarchi (8 years ago)

also apologies, generally speaking, I haven't found a way yet to configure this bloody spellcheck in Epiphany for GNOME 3.14 ... I might park myself until I can write without 3 mistakes per 5 written chars.

Best

# Glen Huang (8 years ago)

Just my 2 cents:

  1. Rick’s field test approach is epic. Encourage me to get my hands dirty. :)

    However, the conclusion I get from these tests is that not many developers explicitly set things to be non-enumerable. This does not directly translate to “many developers care about prototype properties being enumerable”.

    I agree with Andrea here that hasOwnProperty has to be taken into consideration. Especially whether people use hasOwnProperty inside “for in”. Because that’s what enumerability of class syntax will eventually affect. Nothing else matters if I’m not wrong. (This seems to also be Allen’s opinion)

  2. Allen’s study addressed exactly that.

    Especially the "ENUMERATION WITH FOR…IN” chapter. And again, epic. :)

    The conclusion in the study is that not many people use “hasOwnProperty” with “for in”. However, one thing I think is still missing is to examine that what kinds of objects are being used with “for in”. For example, if this is how they use “for in”:

var defaults = { … };
for (var key in defaults) { … }

or

var defaults = { … };
var options = Object.create(defaults);
for (var key in options) { … }

Then it should be totally fine if they don’t use “hasOwnProperty” and they don’t adding things to Object.prototype. (I think whether they (or the libs they use) add properties to Object.prototype should also be examined in this case).

Also “for in” over DOM objects should also be examined. Because enumerability of class syntax doesn’t matter here even if they don’t use “hasOwnProperty”.

So it pretty much comes down to this:

Do many developers use “for in” over objects created by user created constructors and don’t use “hasOwnProperty” when they do that.

If the answer is “yes”, then default class syntax to non-enum is probably not a good idea. Otherwise, it should be fine.

I will do some tests to see if I can answer that question with some concrete data. (probably is going to take some time, I don’t see reliable way of testing that right now).

# Rick Waldron (8 years ago)

To be clear, the only conclusion to be drawn from my exercise was that there isn't a broad pain being felt by all developers all the time, going out of their way to explicitly define non-enumerable properties. My motivation always begins at: how to evolve the language based on the most common developer patterns. In the absence of a compelling hardship, I don't see the benefit of changing the default definition semantics—but—as I my own understanding evolves, I think I'm in agreement with Brendan and Yehuda (and whoever else) that: enumerable in obj lit, non-enumerable in class body is reasonable path forward.

# Axel Rauschmayer (8 years ago)

Got it,

# Glen Huang (8 years ago)

Sorry I got your intention wrong and glad your agreed. :)

# Boris Zbarsky (8 years ago)

On 12/24/14 4:22 PM, Brendan Eich wrote:

We aren't going to reverse-Conway the core language built-ins to have enumerable methods, ever

Sure. Note that this was not one of my three possible listed courses of action.

-- so I think the right attack is on the quirky DOM.

I think the fundamental assumption that the DOM is "quirky" is broken. The DOM is what it is. It dates back on the web just as far as ES does. It was largely implemented (certainly in terms of its ES bindings) by ES engine implementors. The "us vs them" mentality I see so much of on this list is very discouraging, honestly.

Are there some DOM prototype methods/accessors that are non-enumerable?

Not in the Web IDL spec or in Gecko or IE as far as I know.

I believe toString on things like HTMLAnchorElement is not enumerable in Chrome and Safari. See www.w3.org/Bugs/Public/show_bug.cgi?id=26179

I'm not aware of anything else in the "DOM" that's not enumerable.

# Brendan Eich (8 years ago)

Boris Zbarsky wrote:

-- so I think the right attack is on the quirky DOM.

I think the fundamental assumption that the DOM is "quirky" is broken.

Something is quirky if we want mostly-consistent non-enumerability of proto-methods/accessors. Either core built-ins, or DOM. Sorry if "quirky" sounds pejorative, in JS contexts its common enough that what I'm trying to get at is somewhere between unusual and exception-not-rule.

The DOM is what it is. It dates back on the web just as far as ES does. It was largely implemented (certainly in terms of its ES bindings) by ES engine implementors.

Hey, it's me, the JS perpetrator, here :-P. There was no "ES" when I made DOM level 0 (not called that till much later). It was all just JS (spelled out) then. As I noted last time (including how my memory may be failing me), I'm the idiot who made DOM proto-methods enumerable. I think it was mostly an accident; I don't remember a strong rationale.

Note that I did have a rationale for non-enumerability of built-in class proto-methods: for-in utility maximization.

The "us vs them" mentality I see so much of on this list is very discouraging, honestly.

I assure you I was not suffering from us v. them or multiple personalities back then! :-P

Seriously, the main problem over time has been W3C vs. Ecma, but I'm not blaming the standards bodies (only or mostly). It's a separation of concerns that mixed badly with Java heads, XML heads, and the IE monopoly. Bygones.

Back to the present: we have to pick a "default" setting. I think non-enumerability wins, as noted. Can we move forward?

Are there some DOM prototype methods/accessors that are non-enumerable?

Not in the Web IDL spec or in Gecko or IE as far as I know.

I believe toString on things like HTMLAnchorElement is not enumerable in Chrome and Safari. See www.w3.org/Bugs/Public/show_bug.cgi?id=26179

I'm not aware of anything else in the "DOM" that's not enumerable.

Anyone have the jwz nostalgia releases of old browsers such as Netscape 3? Would be fun to poke at some of the old (buggy, shuder) ur-DOM.

# Boris Zbarsky (8 years ago)

On 12/24/14 9:34 PM, Brendan Eich wrote:

Something is quirky if we want mostly-consistent non-enumerability of proto-methods/accessors. Either core built-ins, or DOM. Sorry if "quirky" sounds pejorative

It sure does at least to me.

Hey, it's me, the JS perpetrator, here :-P.

Yes, I know. ;)

I assure you I was not suffering from us v. them or multiple personalities back then! :-P

Good. :)

Seriously, the main problem over time has been W3C vs. Ecma, but I'm not blaming the standards bodies (only or mostly). It's a separation of concerns that mixed badly with Java heads, XML heads, and the IE monopoly. Bygones.

Indeed. I wish this was bygones, though... Would love us to get there.

Back to the present: we have to pick a "default" setting. I think non-enumerability wins, as noted.

That's my gut feeling too, esp. if we can de-Conway.

Anyone have the jwz nostalgia releases of old browsers such as Netscape 3?

If you'd asked this question 3 years ago, when I still had access to some Solaris workstations with all sorts of old stuff on them, I might have been able to do that for you. Certainly Netscape 4.

That said, web searching suggests sillydog.org/narchive/full123.php might have these things. I expect only the Windows versions are at all likely to be runnable on a modern system...

# Boris Zbarsky (8 years ago)

On 12/24/14 9:50 PM, Boris Zbarsky wrote:

That's my gut feeling too, esp. if we can de-Conway.

Also relevant here is www.w3.org/Bugs/Public/show_bug.cgi?id=27361 which never really got a response... I wish the W3C bugzilla had the needinfo feature.

# Andrea Giammarchi (8 years ago)

Considered how this thread started and the exact day of the year a common agreement happened, I believe the following ending would be more than appropriate:

Ho Ho Ho

# medikoo (8 years ago)

Rick, I don't think this kind of analysis while promising, will expose "real" values.

I think most developers which took the step to use descriptors on everyday basis, uses factory tools like that one: www.npmjs.com/package/d

e.g. alone in my packages you'll find hundreds (if not thousands) of properties defined on prototypes as "enumerable: false", but no single definition would be picked by your script.

-- View this message in context: mozilla.6506.n7.nabble.com/classes-and-enumerability-tp329572p331212.html Sent from the Mozilla - ECMAScript 4 discussion mailing list archive at Nabble.com.

# Andrea Giammarchi (8 years ago)

booooo medikoo, you ruined it :D

the opening and the closing of this thread was so semantic, Santa Claus speaking !!!

I believe Rick changed his opinion during this discussion and yes, I agree with you there are tons of utilities/wrapper out there that will try to automate such definition in order to have non enumerable inherited properties in (since I believe that's one of the most desired things since ES3 era)

Best

# Herby Vojčík (8 years ago)

Allen Wirfs-Brock wrote:

On Dec 24, 2014, at 1:49 PM, Kevin Smith wrote:

Here is the summary:
Total Files Read: 11038
Files Containing Explicit 'enumerable: false': 149
Occurrences of 'enumerable: false' (and variants): 206

I love this kind of analysis - thanks!

My interpretation of this data is that non-enumerability isn't important enough to bother with in the vast majority of cases. It may still be that non-enumerability is the least-surprise option for class methods, but certainly users don't care enough about the issue currently to bother with typing in "enumerable: false".

I'm still concerned about the refactoring hazard argument. I think the idea is that if I change some ES5-style class into an ES6-style class, then I need to have good unit tests already in place to make sure that non-enumerability won't break things. Otherwise, I can't do the refactor.

Let's drill deeper into this concern...

There are two parts of dealing with a class-abstraction, the definition the class and consuming instances of the class. Let's look at the enumerability impact on both of those.

Class definition: In an ES5-level class abstraction library, changing the enumerability of methods might have an impact on class creation, if the attraction library does something like method copying using for-in. However, what we are talking about is converting from an ad hoc (and possibly library provided) ES5 class abstraction to using actual ES6 class definition. Class definitions don't depend upon reflection of properties to actually create the class. So, I don't think we have anything to worry about on the class definition side. Enumerability of properties is irrelevant.

Class consumption: It pretty much comes down to whether or not consumers of class instances in ES5 use for-in with or without hasOwnProperty filters to enumerate the properties of class instances. If their ES5 code uses hasOwnProperty to filter out methods, then the fact that ES6 methods are non-enumerable will make no difference. On the other hand, if consumers of these ES5 class instances are doing for-in enumerations and want to see inherited methods then there would be an issue. But, other than for meta programming, it hard to imagine why they would actually want tfor-in to see any methods.

Some folks at Auburn University have been studying a large corpus of JavaScript code, see munawarhafiz.com/research/jssurvey On of the things they've look at is use of for-in and hasOwnProperty. Take a look at the data shown on that page for for-in usage and read sections X and XI in the accompany paper.

Personally, I have always believed we are going down the wrong path by switching (from the original max-in class design) to making methods defined within a class definition enumerable.

Yes, please, if possible, go back to non-enum methods. I was writing at that time as well, but things ended up enumerable. I even cited a real-world example (Amber objects used in jQuery API as options objects), where enum methods broke the functionality, but non-enum methods allowed things to work.

# Brendan Eich (8 years ago)

Herby Vojčík wrote:

Personally, I have always believed we are going down the wrong path by switching (from the original max-in class design) to making methods defined within a class definition enumerable.

Yes, please, if possible, go back to non-enum methods. I was writing at that time as well, but things ended up enumerable. I even cited a real-world example (Amber objects used in jQuery API as options objects), where enum methods broke the functionality, but non-enum methods allowed things to work.

On the agenda for next week, at 4(iii):

tc39/agendas/blob/master/2015/01.md

# Brendan Eich (8 years ago)

Announcement: ES6 class method syntax makes a non-enumerable property of the method's name on the class prototype. That is all. :-)

# Jeremy Martin (8 years ago)

\o/

# Yehuda Katz (8 years ago)

Very glad to see this happen!

# Boris Zbarsky (8 years ago)

Please post to public-script-coord with the proposed plan for web idl.

# Brendan Eich (8 years ago)

I was hoping someone deeper into WebIDL (and with more time for it than I have right now :-P) would do that.

I missed the part of the first-day TC39 meeting where this was resolved, so I hope someone who was there can speak to the WebIDL plan, if any.

# Boris Zbarsky (8 years ago)

Yes, to make this clear, I was talking about people who were at the meeting. I presume that the Web IDL interactions were considered, since they came up in the earlier discussion.

# Yehuda Katz (8 years ago)

Are you asking what to do about the fact that existing WebIDL "classes" produce enumerable properties that probably can't be made non-enumerable without breaking the web?

# Boris Zbarsky (8 years ago)

I think the following things need to be decided:

  1. What should the behavior be for WebIDL objects going forward?
  2. If the going forward behavior is to have properties non-enumerable, should we try to migrate some subset of the existing Web IDL objects to the new behavior?

I'm pretty sure we can't migrate all existing things, at least without breaking someone somewhere, but I suspect we can migrate many of them, if we decide it's actually worthwhile.

I'd be pretty interested in knowing what other implemntors think here, because if we make some decision on #1 I'd rather not have people continue to ship whatever they do now instead of what we decided on. We've been having a lot of that happen with Web IDL. :(

# Yehuda Katz (8 years ago)

To be completely clear, there's nothing that requires DOM to define its objects as using precisely the semantics of ES6 "classes", and indeed, nothing that stops an ES6 class from having enumerable methods:

class Simple {
}

// enumerable

Simple.prototype.method = function() {}

If I understand you correctly, what you're saying is that you would like DOM "classes" to look similar to a normally defined ES6 class.

That is a reasonable desire (and I would love to continue discussing it), but it's not something that's squarely in TC39's wheelhouse (since DOM classes aren't defined using ECMAScript class syntax, which is all that this decision directly affects).

# Boris Zbarsky (8 years ago)

On 1/29/15 4:54 PM, Yehuda Katz wrote:

To be completely clear, there's nothing that requires DOM to define its objects as using precisely the semantics of ES6 "classes"

Sure. There is nothing that requires the DOM to define its objects in any particular way at all. As observed in actual implementations, nothing even requires them to follow the ES spec in any way whatsoever.

At the same time, there seems to be some general agreement in principle that Web IDL should generally look as much like ES6 classes as possible. At least that's my impression.

If I understand you correctly, what you're saying is that you would like DOM "classes" to look similar to a normally defined ES6 class.

I personally quite honestly don't care one whit whether they do. But a number of other people have voiced such a desire, and I'm willing to try to accommodate it. Especially if TC39 is willing to help and actually coordinate things and all that. And if other implementors are willing to actually do so as well.

That is a reasonable desire (and I would love to continue discussing it), but it's not something that's squarely in TC39's wheelhouse (since DOM classes aren't defined using ECMAScript class syntax, which is all that this decision directly affects).

Yes, that's why I suggested that someone who was involved post to public-script-coord, which is the right place for that sort of thing.

# Allen Wirfs-Brock (8 years ago)

My sense, from the informal discussions about this at the TC39 meeting, is that most of us would hope that new WebIDL abstractions follow the ES6 class conventions and that existing WebIDL, because of legacy constrants abstraction are likely not to migrate to the ES6 class conventions.

Syntactically, in WebIDL, you would presumably need an attribute or something to indicate which set of conventions to use for any particular interface.

# Andrea Giammarchi (8 years ago)

awesome news!!! thanks for sharing and also curious to know about other non method properties, including statics.

Just to be aligned, not to complain, I have not a strong opinion except eventually for getters, that should be non enumerable too for consistency with what we have natively (e.g. Array#length)

# Boris Zbarsky (8 years ago)

On 1/29/15 5:43 PM, Allen Wirfs-Brock wrote:

My sense, from the informal discussions about this at the TC39 meeting, is that most of us would hope that new WebIDL abstractions follow the ES6 class conventions and that existing WebIDL, because of legacy constrants abstraction are likely not to migrate to the ES6 class conventions.

OK. Just so we're clear, there are well north of 500 existing Web IDL interfaces defined in the web platform. It will be a while, if ever, before the "new" ones get anywhere close to that.

So what that approach (assuming none of the existing things are migrated) does is basically doom the web platform to always having behavior that authors can't predict. I doubt I can actually get on board with that course of action.... :(

Syntactically, in WebIDL, you would presumably need an attribute or something to indicate which set of conventions to use for any particular interface.

Sure. I'm worried about the goals, not the syntax; the syntax is trivial.

# Yehuda Katz (8 years ago)

On Thu, Jan 29, 2015 at 5:51 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

OK. Just so we're clear, there are well north of 500 existing Web IDL interfaces defined in the web platform. It will be a while, if ever, before the "new" ones get anywhere close to that.

I've personally written a lot of code that enumerates over the properties of an element or other DOM object for some reason or other. I strongly suspect that making existing WebIDL attributes non-enumerable would not be web-compatible (nor, or even in the somewhat distant future).

So what that approach (assuming none of the existing things are migrated) does is basically doom the web platform to always having behavior that authors can't predict. I doubt I can actually get on board with that course of action.... :(

I agree that that sounds bad. The options seem to be:

  1. DOM objects don't behave like classes defined with inline methods
  2. Only new DOM objects behave like classes defined with inline methods
  3. Change all DOM objects to behave that way, if we can do so without breaking the web

I would agree that (2) isn't a good option, and I doubt (3) is web-compatible ... :tears:

# Boris Zbarsky (8 years ago)

On 1/29/15 9:41 PM, Yehuda Katz wrote:

I've personally written a lot of code that enumerates over the properties of an element or other DOM object for some reason or other. I strongly suspect that making existing WebIDL attributes non-enumerable would not be web-compatible (nor, or even in the somewhat distant future).

I agree for elements, and probably all nodes.

On the other hand, I'm fairly sure that no one depends FormData, say...

I would agree that (2) isn't a good option, and I doubt (3) is web-compatible ... :tears:

That's about where I am... and those three options were in fact discussed on this list before.

Ah, well.

# Yehuda Katz (8 years ago)

Right, but that's precisely your point about API predictability. We shouldn't mix and match based on whether we can get away with it, or nobody will have any idea what to expect.

# Boris Zbarsky (8 years ago)

OK, let's just cc public-script-coord here too. For those just joining in: the question is whether Web IDL prototype members should be enumerable given that TC39 just decided that inline-declared methods on classes are not enumerable in ES6.

On 1/29/15 9:45 PM, Yehuda Katz wrote:

Right, but that's precisely your point about API predictability. We shouldn't mix and match based on whether we can get away with it, or nobody will have any idea what to expect.

I think that depends on where we can manage to draw the boundary.

For example, drawing the boundary at "APIs that go to CR after March 15, 2015" or some such is ridiculous because no web developer should need to care about that.

On the other hand, drawing it at "Stuff on nodes is enumerable, stuff on everything else is not enumerable" is something I could at least live with (though there are complications here around EventTarget).

The more complicated the description of what is and isn't enumerable, the worse it is, of course; an actual list of just "whatever we managed to get away with" is no good as you say.

-Boris

P.S. In case I hadn't mentioned it before, there are already UA inconsistencies in what's enumerable right now; e.g. in some UAs toString on various DOM things with stringifiers is not enumerable.

# Andrea Giammarchi (8 years ago)

Developers have to understand they cannot new HTMLDivElement() anyway and they cannot extend WebIDL interfaces in JS world neither.

I am not sure why you see this misalignment that has always been there a problem just now, but also I am not sure I understand why this wouldn't be equivalent:


var HTMLDivElement = new WebIDL({
  yourDefinitionAsObject() {
  },
  get properties() {
  },
  attributeName: 'value'
});

You have already properties and types nobody can set or ensure in native JS ... and I would never stop a programming language from evolving because "another one" (that' how I see WebIDL) is limited or historically different ... I mean, it is different, it has always been, why is this a concern only today?

Genuinely curious, but non enumerable for JS classes is good and I believe it should not be re-changed.

Best

# Anne van Kesteren (8 years ago)

On Fri, Jan 30, 2015 at 7:44 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

Developers have to understand they cannot new HTMLDivElement() anyway and they cannot extend WebIDL interfaces in JS world neither.

That will change.

bz and others have been working on bridging the gap for quite a while now. It's rather frustrating TC39 keeps ignoring the feedback given while at the same time complaining that DOM and IDL are so different.

# Andrea Giammarchi (8 years ago)

this will change too ? NodeList.prototype.propertyIsEnumerable('length')

WebIDL is full of inconsistencies compared with JS world and that, talking about backward compatibility, shouldn't change.

Prototypal inheritance done the old JS way works perfectly and everything is enumerable. I rewrite the example you apparently ignored:


var HTMLDivElement = new WebIDL({
  yourDefinitionAsObject() {
  },
  get properties() {
  },
  attributeName: 'value'
});

That is just fine as everything enumerable, that is just fine as Interface definition.

Why should IDL make classes bad for everything in JS land instead of adopting above pattern in case it does not want to go enumerable?

And again, who actually wants that enumerability and why?

# Boris Zbarsky (8 years ago)

On 1/30/15 1:44 AM, Andrea Giammarchi wrote:

Developers have to understand they cannot new HTMLDivElement() anyway

That will change.

I am not sure why you see this misalignment that has always been there a problem just now

I've seen it as a problem for a while now, as a cursory look at mailing list archives will tell you.

but also I am not sure I understand why this wouldn't be equivalent:

Do you also not understand why ES6 needed to grow enough primitives to explain "host objects" and so forth?

Basically, the fact that we have two somewhat but not quite different things going on is going to be confusing to people, so if we can align them we should.

You have already properties and types nobody can set or ensure in native JS

You can implement the equivalent in native JS nowadays (except for document.all). That's been the point of a lot of the recent evolution of JS. And while the integration between the two is not perfect yet, once of the goals of the ES6 class work has been to improve said integration.

... and I would never stop a programming language from evolving because "another one" (that' how I see WebIDL) is limited or historically different ... I mean, it is different, it has always been, why is this a concern only today?

THIS is one of the big problems I see with how the web platform is developed. Some people view it as multiple independent fiefs to be developed independently, as opposed to a single coherent platform.

Genuinely curious, but non enumerable for JS classes is good and I believe it should not be re-changed.

Did I suggest it should be re-changed? Did you even read anything I wrote in this thread? Please go and do so.

# Boris Zbarsky (8 years ago)

On 1/30/15 7:56 AM, Andrea Giammarchi wrote:

this will change too ? NodeList.prototype.propertyIsEnumerable('length')

Changing that is what we're talking about, yes.

WebIDL is full of inconsistencies compared with JS world and that, talking about backward compatibility, shouldn't change.

The question is whether backwards compat outweighs platform consistency here. For example, does anyone actually depend on "length" being enumerable on a nodelist? I've seen plenty of people working around it, but no one so far depending on it.

That is just fine as everything enumerable, that is just fine as Interface definition.

Trying to align classes and IDL interfaces is a policy decision that was made a while back, fwiw....

Why should IDL make classes bad for everything in JS land

I have no idea what imagined strawman you're replying to here, but it would be good to spell out what your (incorrect, I think) assumptions are if you're going to reply to those assumptions, not to facts.

# Andrea Giammarchi (8 years ago)

Boris, if you are not pushing back the decision about non enumerable methods in ES6 classes then I think we are good.

About everything else I think it's very risky to change old enumerable defaults in IDL because libraries are used to check stuff in Object.keys even to polyfill, instead of going with Object.getOwnPropertyNames so while I believe I won't personally ever be hurt by a change in 'length' or any other enumerable properties in WebIDL once made non enumerable, I am pretty sure that's a very high-risk breaking change for the Web.

The reason classes could have been fixed in ES6 is that these have never even landed officially in JS, WebIDL did already, and as ES3 prototypal like inheritance work in ES6 regardless, anything that requires enumerability can simply use that inheritance style.

New things could use new class ... I think nobody would be surprised, but that's just my opinion.

Best

# Boris Zbarsky (8 years ago)

On 1/30/15 8:43 AM, Andrea Giammarchi wrote:

The reason classes could have been fixed in ES6 is that these have never even landed officially in JS, WebIDL did already

"WebIDL" did nothing of the sort so far. What we have is various legacy behaviors, some of which WebIDL codified. That's what makes things possibly hard to change.

But yes, I'm not sure anything is being added to the discussion here. This is all obvious.

# Brendan Eich (8 years ago)

Please drop the crappy "us" vs. "them" talk. If you read this thread, and others on esdiscuss.org, you can see "TC39" did not "keep ignoring feedback". Yeesh!

We (TC39 includes web developers, W3C TAG members, WHATWG members and cofounders, all well-connected to others working with and on WebIDL) chose to align class prototype method enumerability with ECMA-262 core language built-in method enumerability and clearly-voiced developer expectations, against the old DOM enumerable-method norm, with the intention to bend WebIDL default that way where possible and provide decorator-based or better opt-out in the future.

It's not the end of the world that we live with further differences that have long stood between core language and browser method enumerability. What would be worse in our judgment: enumerability by default.

# Brendan Eich (8 years ago)

Brendan Eich wrote:

Please drop the crappy "us" vs. "them" talk. If you read this thread, and others on esdiscuss.org, you can see "TC39" did not "keep ignoring feedback". Yeesh!

In case it is not clear: Andrea is not on TC39, not "us" or "them" in your dichotomy, and while I agree he disregarded (if not disrespected) ongoing work to make new HTMLDivElement work -- and that wasn't cool of him -- that has zip to do with this decision, or with "TC39".

# Andrea Giammarchi (8 years ago)

I didn't mean to disrespect anybody work here, I am just personally a bit tired of TC39 decisions that, after being already made and widely discussed in the same thread, are complained/disregarded regardless everyone agreed already on an official meeting.

I am not TC39 and not even close to WHATWG and didn't know there was an effort to make new HTMLDivElement and all other constructors possible, but I am also one that created the widely adoptable/backward compatible polyfill for Custom Elements specifications and its document.registerElement and haven't seen anything even close to that "extendability" from the DOM world in JS published these years so ... yeah, I might have misunderstood that having better classes in JS was seen as a bad thing from WebIDL prospective and, accordingly, I've (over)reacted as developer that does things daily to live and cannot work behind a "we are trying to align" future promise: I kinda need things to be the right way today or ASAP, and I'd be surely happy if tomorrow these will be even better!

Do we agree this class decision was a good one? Perfect, then let's move on to another thread and discuss how things could be better from "both worlds" point of view in the future.

Best

# Brendan Eich (8 years ago)

Andrea Giammarchi wrote:

Do we agree this class decision was a good one? Perfect, then let's move on to another thread and discuss how things could be better from "both worlds" point of view in the future.

I'm not sure why you replied to me, since I (and everyone on TC39) has agreed to make the decision for class methods to be non-enumerable, so we must all agree that decision was a good one! :-P

Probably you're replying to Anne, indirectly.

# Yehuda Katz (8 years ago)

Worth noting: DOM "classes" won't be the only objects a JavaScript user interacts with that have enumerable methods. Virtually 100% of class libraries in JavaScript, including ones created after ES5, create classes with enumerable methods.

I don't think that this change has a prayer of making enumerable methods feel "weird" in JavaScript any time soon.

# Andrea Giammarchi (8 years ago)

Uhm...yes, Anne or Boris, or anyone behind IDL that might have felt disrespected.

Best