super, methods, constructors & Co.

Luke Hoban lukeh at microsoft.com
Mon Jun 4 00:41:36 PDT 2012


>>  In fact, without <|, are there any cases where super behaves correctly in an object literal, other than making super calls to Object.prototype functions?

> We haven't eliminated the ability to define object literals that inherit from objects other than Object.prototype.  We have just changed the syntax for specifying them from:
>   proto <| {}
> to
>    {__proto__: proto}

Good point.  Object literals with inline __proto__ declarations do provide method contexts where super will behave correctly. But I think it's fair to assume that most object literals will not include inline __proto__ declarations.  

> In general, if you have multiple level instance based inheritance hierarchies, super has the same general utility that it has in an a class/instance based inheritance hierarchy. 

But only if that multi-level instance hierarchy is created in a very specific (and arguably uncommon) way.  That's my concern.  We happen to be discussing adding a new syntactic construct for classes, and can make sure that 'super' always works inside classes.  But we can't make 'super' always work inside object literals.  

>> 
>> Take Backbone/Underscore as an example.  A developer writes some code like:
>> 
>> var Note = Backbone.Model.extend({
>>    set: function(attributes, options) {
>>        super.set(attributes, options);
>>    }
>> });

> Of course, the above is a case where I suggest a mustache should probably be used.  

Although it isn't obvious from the code I shared, this is actually creating a new object which has Backbone.Model in its prototype chain, and putting a set method in the new instance.  So mustache doesn't easily integrate into the syntax here.  One would need to change the API model non-trivially, and the consumption syntax somewhat significantly, to use mustache here.

> If extend did not explicitly deal with rebinding super referencing methods (which it can't, if we eliminate defineMethod) then the set method would throw when it tried to invoke the super.set.  So, the code fails with an exception rather than silently working incorrectly.

That is at least better - but late errors trying to use a 1st class piece of syntax in a seemingly meaningful location feels concerning.

> Also, a very similar issue will apply to if the argument to extend had any private name properties.  So this isn't a problem that is specific to super. I can actually envision how it may be easier for a built-in extend function to handle super rebinding than it would be for it to handle private names.  I don't currently see how a non-built-in Es<=5 semantics based extend could handle it.

That's a good point, and does lead me to be concerned about the behaviour of ES3-style extend on private names as well.  For similar reasons, I expect devs will just assume this sort of thing works.

> The issue of passing new semantic constructors into legacy frameworks is certainly worth thinking about.  Although, i tend to think that actively maintain frameworks will get ahead of the ES6 adoption curve.  And that legacy using a out of date framework is much less likely to start using new ES6 constructs.

This seems optimistic, but possible.  More importantly though, do we know how these libraries would address this sort of thing?  And what the transition would look like (can they do it in a library-only way such that they can feature detect to work correctly with new ES6 constructs, but still work in pre-ES6 environments)?

>  Of course, the above doesn't even work for ES5 since it it ignores non-enumerable properties, doesn't copy accessor properties correctly, and probably unintentionally includes inherited properties. We should be cherry picking super as the only feature that is problematic for such code.
> One might speculate if these deficiencies aren't causing problems today, then perhaps the issue of passing new language constructs into to legacy frameworks that don't correctly deal with them is not a significant problem.

Good point.  Though one might also speculate that none of the ES5 features mentioned have seen enough adoption on the web yet to understand the full impact of these problems.

But I think a difference that makes me more concerned about super is that this is something these APIs are trying to do already today.  The backbone.js docs [1] that I took this example from explicitly call out __"Brief aside on super: JavaScript does not provide a simple way to call super — the function of the same name defined higher on the prototype chain. If you override a core function like set, or save, and you want to invoke the parent object's implementation, you'll have to explicitly call it..."__.  It gets a bit more subtle to say "JavaScript has a way to call super, but it won't work here."   

> Here is another way to think about this class of issues. super is a language feature that is design to work at the level of the built-in behavior composition mechanism of ES (a fixed single inheritance [[Prototype]] chain).  _extend and other abstraction impose a different behavioral composition model.  super may not work as expected with those other models unless their implementation takes it input account in their implementation.

That's a fair summary.  In practice though, a lot of object models in JavaScript are built using these higher-level abstractions built on top of the core JS prototype chain.  Mostly, features at the core language level continue to be usable via these higher level abstractions.  It's concerning to add core language features that can't (or can't sufficiently easily) be used by the common abstractions.

Perhaps the ultimate question is just - what is the best an existing library developer offering a JS object model abstraction (like extend) can do to support super?

Luke

[1] http://backbonejs.org/


More information about the es-discuss mailing list