Bound instance-function vending (was RE: Arrow binding)
There's a big penalty even in optimizing VMs in adding (the same) method reference to every instance, vs. sharing it via a prototype. Binding |this| without new syntax, using whatever (closures, .bind), can be optimized but it's a challenge and mainly, users do not do it.
The functional-combinator OOP style Angus advocates, which relies on .call to delegate, is pretty but it's yet another discipline and not one I see catching on. The language doesn't make it any easier for users than for optimizing VM implementors.
The pattern you show below, the "closure pattern", is advocated (e.g. by crock's book) and it is used. The cost doesn't matter at low object populations and it's pretty enough, besides being already there, that I agree it will be hard to beat in my view.
Apologies for sounding schizo. I'm saying prototypal wins at scale and I see it more often used, but closure has its fans and it's used too.
With both (plus other approaches such as Angus's) I do not think it's a given that in the future, JS will make methods auto-bind |this| via prototypal accessors. That is possible today (ES5 and earlier getter/setter support in most browsers) and it's quite rare -- I can think of only a few cases, one of which is in the new I18N library.
On Apr 23, 2012, at 11:17 AM, Brendan Eich wrote:
Apologies for sounding schizo. I'm saying prototypal wins at scale and I see it more often used, but closure has its fans and it's used too.
With both (plus other approaches such as Angus's) I do not think it's a given that in the future, JS will make methods auto-bind |this| via prototypal accessors. That is possible today (ES5 and earlier getter/setter support in most browsers) and it's quite rare -- I can think of only a few cases, one of which is in the new I18N library.
And note that the I18N usage is for a situation where the property is vending (to use Alex's term) a function that is intended to be used as a call-back that needs access to the state of the instance it was accessed from. In particular, it enables someone to say:
someArray.sort(myCollator.compare)
rather than someArray.sort(myCollator.compare.bind(myCollator)
This seems like an important idiom hat may become even more important in the context of ES6. We probably need to encourage broader education about this idiom. But it isn't clear that it needs special syntax or even what that might be.
Allen Wirfs-Brock wrote:
This seems like an important idiomhat may become even more important in the context of ES6. We probably need to encourage broader education about this idiom. But it isn't clear that it needs special syntax or even what that might be.
Dave wrote
for this and it's a live strawman (albeit in conflict with guard syntax, or perhaps just reusing it outside of binding contexts). Agree it's not ready for ES6, of course.
Hmm, so to make sure I understand: the getter-vending is more efficient because it only creates new functions when they are "gotten" (e.g. called or passed to a callback), whereas the closure pattern creates new functions for each instance? Or is there another reason?
It seems like, even if I did create 100 objects, if I called method
twice on each of them, the closure pattern would win on efficiency.
I suppose it also helps enforce idiomatic expectations, namely that methods are on the prototype. Then again it breaks the idiomatic (and I'd argue more basic) expectation that myObj.method === myObj.method
, not to mention myObjInstance1.method === myObjInstance2.method
.
Both of these could be ameliorated with WeakMap caching, but of course virtually nobody is going to write that code. (Unless... time for yet another class library? Hehehe.)
Domenic Denicola wrote:
Hmm, so to make sure I understand: the getter-vending is more efficient because it only creates new functions when they are "gotten" (e.g. called or passed to a callback), whereas the closure pattern creates new functions for each instance? Or is there another reason?
That's one reason.
Even better would be a way to avoid creating a function per method per instance unless a method "get" (not "call") occurs. Calls that do not lead to reflection on the callee's identity as a function object should not need to create (and memoize, see below) a bound function for the method call. Only for extraction via get.
It seems like, even if I did create 100 objects, if I called
method
twice on each of them, the closure pattern would win on efficiency.
No, in any such prototype-getter-makes-bound-function scenario, we would need memoization, so that
o.m === o.m
(Edit: I see you mention this below.)
I suppose it also helps enforce idiomatic expectations, namely that methods are on the prototype.
You mean by "it" getter-vending? Not really. The getter has to memoize a function bound to the instance on which the proto-property was "got". That must be stored somewhere. Simplest is to store on the instance itself, shadowing the getter. That makes getter-vending a lazy variant of the closure pattern.
Contrast to Smalltalk or Dart where classes preclude overriding, shadowing and deleting properties along the prototype chain. This enables separation of |this| from method lookup in a vtable, so there's no need for more than a vptr per instance (with single inheritance).
An aggressive JS VM could optimize likewise but would have to deoptimize whenever an observable change to the composite vtable due to assignment (what I meant by overriding above), shadowing, deleting happened.
Then again it breaks the idiomatic (and I'd argue more basic) expectation that
myObj.method === myObj.method
, not to mentionmyObjInstance1.method === myObjInstance2.method
.
That's not idiomatic. An idiom is a phrase such as "kick the bucket" where a non-English reader cannot divine the meaning from the constituent words.
Whereas method object identity invariance is kind of basic, not just for users poking about and saving a reference for later, but for efficiency.
Both of these could be ameliorated with WeakMap caching, but of course virtually nobody is going to write that code. (Unless... time for yet another class library? Hehehe.)
Right. An elaborate simulation of vtables using proto-getters, weakmaps (for memos), shadowing in instances could be done, but who would use it?
If the idea is that ES6 or 7 has classes and the vtable optimization becomes more usable and easier for implementors, maybe. But such a system would not be equivalent to constructors with prototypes, and it would not observably use proto-getters vending memoized per-instance functions (because that is not the vtable optimization, rather it is the closure-pattern deoptimization).
So I don't buy that in the future everyone will use prototype getter-vending machines :-P. If in the future something new more like class-oriented languages with vtables under the hood emerges, it won't be fully compatible with prototypes as used in JS today. And that is a reason to doubt it will emerge, in my book.
Brendan Eich wrote:
No, in any such prototype-getter-makes-bound-function scenario, we would need memoization, so that
o.m === o.m
(Edit: I see you mention this below.)
The real problem is better captured by showing the prototype p and two instances, o and q:
p.m === p.m
should be true. So should
o.m === o.m
and
q.m === q.m
but what about
o.m === p.m
and
o.m === q.m
JS requires distinct bound function identity from the unbound p.m identity, so false and false.
If we managed to extend JS with a classy declarative syntax that enabled vtables, while keeping functions first class (at the price of prototype-chain mutation: assignment to override, or shadow; also of course delete), then we might well want true and true. But there would be no way in JS itself to implement such magic in terms of properties (data or accessor, doesn't matter) with function values.
This is why I continue to think that we won't see what Alex predicted: prototype getter method vending. We haven't see it much in the wild yet either.
I'm sorry that I could not reply to the multiple replies I got last weekend. Simply can't get the time - plus I have a phobia of fan-out replies (an issue with mail-based discussion groups?)
In any case here's a potted reply that addresses at least some of the points:
- Kevin et al suggested YAGN call/apply on (non-method) functions. Here's a pretty neat example where call/apply is a perfect fit for standalone functions - and would break if replaced with fat arrows.
It's a simplified version of a a localStorage function (itself a mixin BTW) which defines standard encoding methods and then mixes in whatever storage technique is available to the browser. (An added bonus of having functions here, aside from privacy, is that we can employ hoisting to move the implementation nitty-gritty to the bottom, so as not to obscure the core process)
- Brendan et al pointed out that we already have hard |this| binding in the form of Function::bind.
Yes, but bind is so blatantly explicit in its intention, the probability of surprise is almost certainly less than with arrow functions, whose hard lexical binding will surely come as a surprising side effect to many.
- Several people suggested that there is a strong desire for a more intuitive form of |this| binding.
Yes, people get confused by this binding; even though it is not hard to learn the rules, and I know them very well, I still trip over them sometimes. But unless we are going to introduce a "hella strict" mode that reverts all previous rules of |this| binding, yet another rule will just add to the morass.
Most of the "yay, fat arrow" comments I've seen from the dev community are celebrating its brevity, I expect many of the authors have zero knowledge of the lexical binding implications (why would they, unless they came from CoffeeScript?). Now matter how much we justify fat arrow behavior as part of a long term vision, to many it is going to be a hidden, and unwanted side effect.
At least introduce thin arrow at the same time, as a carrot to lure who just want (and just expect) an abbreviated function syntax.
- Kevin et al suggested YAGN call/apply on (non-method) functions. Here's
a pretty neat example where call/apply is a perfect fit for standalone functions - and would break if replaced with fat arrows.
This is a great example. Two points:
1.) In this case, I think it's going to be pretty difficult to prove that a "dynamic this" arrow function would be any more readable or "better" than current syntax:
// On what basis is this:
this.initialize = () -> { ... };
// any better than this?
this.initialize = function() { ... };
There are a couple of characters saved, but it's less readable. Why introduce new syntax for such dubious gain?
2.) For mixin stuff like your example, I think the elegant solution is "object literal extension" syntax:
https://gist.github.com/2521128
I'm not sure of the status of that syntax, but hopefully it will get included in ES6.
Kevin Smith wrote:
2.) For mixin stuff like your example, I think the elegant solution is "object literal extension" syntax:
I'm not sure of the status of that syntax, but hopefully it will get included in ES6.
What if the ES6 solution were a standardized Object.extend, instead?
What if the ES6 solution were a standardized Object.extend, instead?
(Assuming the same semantics...)
It's a little more wordy but still looks pretty good to me for this use case.
On Sat, Apr 28, 2012 at 11:13 AM, Angus Croll <anguscroll at gmail.com> wrote:
- Kevin et al suggested YAGN call/apply on (non-method) functions. Here's a pretty neat example where call/apply is a perfect fit for standalone functions - and would break if replaced with fat arrows.
It's a simplified version of a a localStorage function (itself a mixin BTW) which defines standard encoding methods and then mixes in whatever storage technique is available to the browser. (An added bonus of having functions here, aside from privacy, is that we can employ hoisting to move the implementation nitty-gritty to the bottom, so as not to obscure the core process)
This is a mixin, which means that it needs methods, not functions. Fat arrows aren't meant for methods.
This should rightly use 'function' (which is meant for methods), or eventually use a mixin syntax. (We should keep this in mind as an example of mixins, so we don't accidentally forbid the ability to choose which mixin to use dynamically, like this does.)
Tab Atkins Jr. wrote:
This should rightly use 'function' (which is meant for methods), or eventually use a mixin syntax.
See the version Kevin did using Object.extend and method definition shorthand:
The following two rules should be everything one has to know regarding callable entities, in ES.next:
-
Method:
this
is an implicit parameter => use a method definition. -
Non-method function:
this
is not an implicit parameter => use an arrow function.
In this light, I’d rewrite Kevin’s code slightly, because, conceptually, localStorage
is a non-method function:
const localStorage = (obj) => {
Object.extend(obj, {
I think Angus’ most important point is this:
Yes, people get confused by this binding; even though it is not hard to learn the rules, and I know them very well, I still trip over them sometimes. But unless we are going to introduce a "hella strict" mode that reverts all previous rules of |this| binding, yet another rule will just add to the morass.
It would be my hope that we can replace all previous rules with the two rules above. apply
will mostly be replaced by the spread operator, call
will mostly be replaced by calling a value. Some code such as forEach
implementations that previously needed to use call
to simulate lexical scoping don’t need a thisValue
parameter for array functions.
Then the question remains: What else is possibly confusing? Using function* as a name for generators might be problematic.
Most of the "yay, fat arrow" comments I've seen from the dev community are celebrating its brevity, I expect many of the authors have zero knowledge of the lexical binding implications (why would they, unless they came from CoffeeScript?). Now matter how much we justify fat arrow behavior as part of a long term vision, to many it is going to be a hidden, and unwanted side effect.
True. I would ague that partitioning callable entities into the two categories mentioned at the beginning is natural and will allow you to forget about binding rules.
I am not saying that the transition from the old rules to the new rules will be entirely painless, but if the new rules are simple, that pain is worth it, IMHO. Library code might need to go to extra lengths to help normal developers with the transition (error messages, different behavior, tool functions, etc.) and – to be explicit – might need a predicate such as isArrowFunction
(which should only ever be used under the hood and thus would not increase confusion for library users).
Correction:
Some code such as forEach
implementations that previously needed to use call
to simulate lexical scoping don’t need the thisValue
parameter for arrow [was: array] functions.
Axel Rauschmayer wrote:
I am not saying that the transition from the old rules to the new rules will be entirely painless, but if the new rules are simple, that pain is worth it, IMHO.
There are no new rules. Some functions ignore |this| or bind it rather than using it. These differences in kind do not change due to arrows.
Library code might need to go to extra lengths to help normal developers with the transition (error messages, different behavior, tool functions, etc.) and – to be explicit – might need a predicate such as
isArrowFunction
(which should only ever be used under the hood and thus would not increase confusion for libraryusers).
There's no point in such a misnamed predicate. Today we have functions that ignore or bind |this|, as well as those that use |this| passed by the caller. Code requires and assumes one kind or another and does not test (and most programmers wouldn't cover all cases, and shouldn't have to).
Part of an API, an essential part of the contract, is any function parameter's |this| binding. Arrows make it easier to use callbacks wanting lexical |this| or not using |this| at all. This covers a large cohort. Code invoking the arrow is not going to test well or at all and do something helpful, or unhelpful such as throwing an exception.
With “rules”, I don’t mean rules in the sense of the language spec, but rather rules for teaching the language to newcomers.
On Apr 28, 2012, at 22:24, Axel Rauschmayer <axel at rauschma.de> wrote:
The following two rules should be everything one has to know regarding callable entities, in ES.next:
- Method:
this
is an implicit parameter => use a method definition.- Non-method function:
this
is not an implicit parameter => use an arrow function.In this light, I’d rewrite Kevin’s code slightly, because, conceptually,
localStorage
is a non-method function: const localStorage = (obj) => {Object.extend(obj, {
If I understand correctly, this approach negates the need for a mixin function entirely - localStorage could just be an old style mixin hash.
But either way this tosses out the benefits I get from functional mixins. By invoking the mixin instead of copying it I can a) customize it's behavior by passing it arguments b) have the mixin add "advice" (before, after, around) to functions of the target object.
I think Angus’ most important point is this:
Yes, people get confused by this binding; even though it is not hard to learn the rules, and I know them very well, I still trip over them sometimes. But unless we are going to introduce a "hella strict" mode that reverts all previous rules of |this| binding, yet another rule will just add to the morass.
It would be my hope that we can replace all previous rules with the two rules above.
apply
will mostly be replaced by the spread operator,call
will mostly be replaced by calling a value. Some code such asforEach
implementations that previously needed to usecall
to simulate lexical scoping don’t need athisValue
parameter for array functions.
I admire the vision but in reality the boundary between ES5 and ES6 will be messy and drawn out - there will be no clear cut switching point. Legacy code must live alongside the new vision. Without a new strict mode which banishes much of the old spec (and kills my functional mixins) I'm finding it hard not to picture chaos. (and how do you shim => anyway?)
On Apr 29, 2012, at 1:28, Brendan Eich <brendan at mozilla.org> wrote:
Axel Rauschmayer wrote:
I am not saying that the transition from the old rules to the new rules will be entirely painless, but if the new rules are simple, that pain is worth it, IMHO.
There are no new rules. Some functions ignore |this| or bind it rather than using it. These differences in kind do not change due to arrows.
Is there lexical |this| binding in ES5?
Angus
If I understand correctly, this approach negates the need for a mixin function entirely - localStorage could just be an old style mixin hash.
But either way this tosses out the benefits I get from functional mixins. By invoking the mixin instead of copying it I can a) customize it's behavior by passing it arguments b) have the mixin add "advice" (before, after, around) to functions of the target object.
For me, conceptually, localStorage() is a non-method function that has a single parameter. I don’t think the capabilities are impaired if you change each occurrence of: this.foo = function (...} {...}; to Object.extend(obj, { ... foo(...) { ... }, ... });
Object.extend would automatically invoke Object.defineMethod() (or similar) for you, to enable super-references (but I doubt there is much use for them in mixins).
Admittedly, if there is only a single method then there is a lot of unnecessary syntactic noise. However, methods tend to appear in groups (implementing protocols). Putting such groups in object literals makes much sense.
As an aside: If you want to annotate methods then function expressions are indeed a plus, because they can be immediately invoked. Python has method decorators for this [1].
myExtend(obj, { someMethod: function (inner, arg) { doSomething(); var result = inner(arg); result++; return result }.around() });
A bit hacky, but nicely declarative. Function.prototype.around() would tag the method, later post-processing would perform the transformation.
[1] www.python.org/dev/peps/pep-0318
It would be my hope that we can replace all previous rules with the two rules above.
apply
will mostly be replaced by the spread operator,call
will mostly be replaced by calling a value. Some code such asforEach
implementations that previously needed to usecall
to simulate lexical scoping don’t need athisValue
parameter for array functions.I admire the vision but in reality the boundary between ES5 and ES6 will be messy and drawn out - there will be no clear cut switching point. Legacy code must live alongside the new vision. Without a new strict mode which banishes much of the old spec (and kills my functional mixins) I'm finding it hard not to picture chaos. (and how do you shim => anyway?)
Right. I think you can still tell a simple story to beginners while supporting thin arrow or (this, ...) => {} for legacy software.
But I’d hope that the old code can be cleanly separated and that a new coding style will be supported by static checking tools such as JSLint.
1- Yes, several time that you mention it, and ++ from me each time
2- I prefer the dot notation rather than Object.extend, even if both are good
Le 28/04/2012 20:49, Kevin Smith a écrit :
If so then there is no need for any isArrowFunction or isFunctionThatBindsOrIgnoresThis predicate.
Angus Croll wrote:
On Apr 29, 2012, at 1:28, Brendan Eich<brendan at mozilla.org> wrote:
Axel Rauschmayer wrote:
I am not saying that the transition from the old rules to the new rules will be entirely painless, but if the new rules are simple, that pain is worth it, IMHO. There are no new rules. Some functions ignore |this| or bind it rather than using it. These differences in kind do not change due to arrows.
Is there lexical |this| binding in ES5?
The only relevant issue for my exchange with Axel was whether any kind of this-binding exists in ES5 or older. The answer is yes.
"Lexical this" is often wanted and of course simulated with var self = this; ... function (){...self...} or .bind (implementable in pure ES3). But the lexical part doesn't enter into the contract of the API taking the function argument. It's between the API caller and the function argument's implementation -- when the latter is a closure in the former, lexical |this| often wins.
Brendan Eich wrote:
But the lexical part doesn't enter into the contract of the API taking the function argument. It's between the API caller and the function argument's implementation -- when the latter is a closure in the former, lexical |this| often wins.
What does enter into the contract of the API taking the function argument is whether |this| is dynamically bound and passed with the expectation that the funarg not use some other |this|. But even then, the funarg is free to ignore |this|.
Between lexical-this and no-this, Kevin's analysis shows 80-90% (high end helped by method definition shorthand) coverage.
Thinking about arrows as a new thing to teach is fine, but we should not teach myths. |this| binding in JS today requires careful thought in all the usual scenarios: constructor, method, callback, global or local function. You can see why some advocate no-this uber alles!
But lexical-this is (if memory serves) around 40-50% gross of the 80-90% Kevin studied. That's big and we know people get that wrong.
If there's a strong use-case for -> as function shorthand, we should
consider adding -> too. But the use-cases don't look strong enough yet,
at least not to me (and I'm sympathetic!) to go back to TC39 asking for -> on top of =>.
Then I guess we need another survey.
"What does the JS arrow function proposal mean to you? a) An abbreviated function syntax b) A hard bound lexical |this| binding c) Nothing"
I'd gladly be proved wrong but my guess is most JS developers who don't answer (c) will answer (a). (I see plenty of supporting evidence on Twitter). Thin arrow would be an excellent decoy for those who expect (or want) just (a).
This is also in response to Kevin's suggestion that arrow syntax is barely more concise than regular function syntax. I don't think that's the perception in the wider dev community, neither is it mine (in the best case we save four ways - no function keyword, no braces, no argument parens, no return stmt).
Personally I expect I can get by using function keyword where using => will
break my idioms - but its a bit much to expect everyone to understand and react to these subtleties. I guess I'm trying to anticipate future cowpaths and pave them before they make too much mess.
Use case: error checks. For example: forEach could complain if it got both an arrow function and a thisValue. One could also complain if one expected a function with dynamic/unbound this
and got a function with bound this
, instead. To be extra strict, one could even complain when an unbound function is provided where a non-method function is expected (because either an arrow function should be used or a method’s this
should be bound). All of these are things that you do as an API implementer, not necessarily as an API user.
Static checking would be even better, but then we would need a way to declare whether an argument is expected to have dynamic or lexical this.
Possibly
Angus Croll wrote:
Then I guess we need another survey.
What was the first survey?
Kevin's analysis was on code, using a tool to classify functions as no-this/lexical-this vs. the rest.
"What does the JS arrow function proposal mean to you? a) An abbreviated function syntax b) A hard bound lexical |this| binding c) Nothing"
This is all subjective and (without implementations to user-test) prospective. A survey is not going to be helpful IMHO. Rorschach test...
I'd gladly be proved wrong but my guess is most JS developers who don't answer (c) will answer (a). (I see plenty of supporting evidence on Twitter). Thin arrow would be an excellent decoy for those who expect (or want) just (a).
What do you mean by "decoy"?
This is all kind of twitter-ific, and so pretty much useless.
This is also in response to Kevin's suggestion that arrow syntaxis barely more concise than regular function syntax. I don't think that's the perception in the wider dev community, neither is it mine (in the best case we save four ways - no function keyword, no braces, no argument parens, no return stmt).
You mean where Kevin wrote:
""" // On what basis is this: this.initialize = () -> { ... };
// any better than this?
this.initialize = function() { ... };
There are a couple of characters saved, but it's less readable. Why introduce new syntax for such dubious gain? """
with a space before the -> and one before { in both alternatives, and
with the parameter list costing the same no matter what, there are five characters saved (' ->' vs. 'function').
Five is good, I like it (I like thin arrows and proposed them along with fat arrows initially). But if this use-case is rare enough due to => and
method definition shorthand (combined with Object.extend or better where possible), it may not be worth the added cost of ->.
What's the cost of adding ->? Mainly users having to remember when to
use which, and getting it wrong. That's not a trivial concern, and it kept -> out of consensus at the last TC39 meeting.
Personally I expect I can get by using function keyword where using => will break my idioms - but its a bit much to expect everyone to understand and react to these subtleties. I guess I'm trying to anticipate future cowpaths and pave them before they make too much mess.
Doing nothing leaves the cows falling off the wrong-this cliff. Doing something means adding syntax, there's no way to make a breaking change. Adding two things instead of one makes not only two things, but the problem of choice.
We may get -> yet, don't get me wrong. But it won't come from surveys or
twitter samples.
Axel Rauschmayer wrote:
Use case: error checks. For example: forEach could complain if it got both an arrow function and a thisValue.
Do you mean error, or warning?
Because you can use forEach with a function that ignores its |this|, and pass the thisValue optional parameter too, without error today. I see no good coming from trying to add an arrow-only error in the future when this case is not an error.
A bit on Object.extend(obj,{...}) vs. obj.{...}
To get started, just a reminder that we can't really compare the two based on the assumption that the second argument to extend is an object literal as there is no way to syntactically guarantee that. We have to assume that both arguments to Object.extend are preexisting object that were created in some arbitrary manner. EG, we are have to compare Object.extend(obj1, obj2) vs. obj1.{...}
On Apr 29, 2012, at 10:10 AM, Axel Rauschmayer wrote:
... Object.extend would automatically invoke Object.defineMethod() (or similar) for you, to enable super-references (but I doubt there is much use for them in mixins).
Mixin's and inheritance (including) super references are orthogonal concepts. Some may choose to use only one or the other but they really need to be composable for for those who need to use them in combination.
The defineMethod point is important. Equally important is the treatment of private named properties.
To correctly preserve super invocation semantics methods must be processed using Object.defineMethod when they are copied from obj2 to obj1. For obj1.{...} this isn't a problem because the "extension object" and its properties don't preexist. They are just syntax that is processed just like creating completely new properties on obj1 and the semantic rules for those new properties are exactly the same as those that would have been used in an object literal that was providing the initial definition of obj1. There is no copying semantics of worry about.
One of the syntactic distinctions that is made by obj1.{...} is the difference between a "method" property and a function valued "state" property. Consider:
//somewhere else in the program vender.getSomeCallback = function(a,b) { let cb= function() {...myWeakMap.get(cb).attachment...this.something(a,b)....}; myWeakMap.set(cb, generateAttachment(a,b)); return cb; }
//main example obj1.{ callback: vender.getSomeCallback(x,y), meth() { super.meth(); this.callback.call(this.target); }
Note that .{ sees the callback property as a regular "state" property and doesn't use defineMethod semantics to create it on obj1. The value returned by vender.getSomeCallback is directly used as the value of the callback state property and the identify based annotation mechanism used in the definition of the callback will work just fine. meth, because it is expressed using concise method syntax is processed as a new method definition that is super bound as if by defineMethod to obj1.
Now consider using extend:
let obj2 = { callback: vender.getSomeCallback(x,y), meth() { super.meth(); this.callback.call(this.target); };
Object.extend(obj1,obj2);
Object.extend doesn't have any visibility of how the properties of obj2 were defined. All it sees is two data properties whose values are functions. It if used Object.defineProperty to process both properties then methwould be super bound to the wrong object. If it used Object.defineMethod on both properties then the callback function's identify may change. What Object.extend will have to do is have a heuristic (if the value is super bound to obj2, then defineMethod it to obj1. Otherwise, just use defineProperty) that it applies to function valued data properties. This heuristic should be adequate for uses that correspond to what could be expressed using .{ but it also allows other cases where the heuristic may produce something other than the desired result. Those situations simply don't exist for .{
Use of private names with Object.extend is even more problematic.
Consider something like:
let MixinArrayIterator = (obj) => obj.{ @iterator() { let coll = this; return function*() {...yield coll[...} } }
which uses the built-in @iterator private name. This works nicely with .{ because the private name is used within the .{ special form and doesn't require any runtime reflection on the mixin "object". However, to do the same with Object.extend would requires that extend has reflective visibility of actual private named properties of obj2. The reflection restrictions on private names exists to support various high-integrity use cases. In this particular situation, there is nothing actually "private" in the hight-integrity sense about @iterator. To make this all work with extend we would probably have to re-introduce the concept of "unique names" which are like private names but without the reflection restrictions. That then would introduces additional complexities.
It is probably possible to support mixin attachment using a functional form (Object.extend) rather than using a special form (.{} ) but there is a lot more to it than just a simply syntactic substitutions.
This sounds lovely. I vaguely remember seeing it on es-discuss in the past (for one of the built-ins or proposed built-ins, maybe?). But never in the wild.
I'm curious, though---what advantages does this have over simply setting the method as an instance property? I.e. how does it differ from the following pattern?
function C() { var that = this; that.method = function () { console.log(that); }; }