On Apr 29, 2010, at 11:44 AM, Garrett Smith wrote:
On Thu, Apr 29, 2010 at 3:01 AM, Alex Russell <[email protected]>
wrote:
From 15.3.4.5:
4. Let F be a new native ECMAScript object .
I can't discern any particular reason why equivalence should succeed
for multiple calls to bind() from the spec. What am I missing?
The bound function that is returned can be saved as a property of
something, so that it can be later removed.
var f = obj.method.bind(obj);
node.addEventListener("click", f, false);
node.removeEventListener("click", f, false);
Sure, you can save the bound method reference. It should remain a
first-class function -- this is in contrast to languages with only
methods on classes and no way to extract them as funargs to pass
around, rather than invoke (even .apply wants an extracted funarg).
This raises a point I mentioned in https://mail.mozilla.org/pipermail/es-discuss/2010-February/010830.html
et seq., namely that bound methods are not enough to avoid a million
moveTo methods if you have a million Sprite object instances, each
method bound to its instance as receiver (|this|):
let sprites = [];
for (let i = 0; i < 1e6; i++)
sprites[i] = new Sprite(i);
If Sprite is written conventionally:
function Sprite(i) {
let sprite = {index: i, ...};
sprite.moveTo = (function (x,y) {...}).bind(sprite);
return sprite;
}
then implementations burn a lot of unjoined function objects just to
associate |this| with each sprite in sprites[i].moveTo(x, y).
Classical OOP languages can avoid this in the call-expression use case
(where no funarg is extracted). Even ignoring static typing or type
inference, if the callee Reference base is the object from which the
method identified by the Reference propertyName was extracted, then no
new function object need be allocated to associate that receiver with
that method (ignoring arguments.callee and other identity-leaking
hazards in JS).
How could the callee type be wrong? In JS, it could be due to funarg
extraction:
let f = s.moveTo; ...; f(x, y)
will bind |this| to the global object (non-strict) or undefined
(strict).
let o = {moveTo: s.moveTo};
...
o.moveTo(x, y)
or
s.moveTo.call(o, x, y)
likewise would try to bind |this| to o when activating moveTo. Any
scheme to prevent these receiver override attempts requires allocating
a function object that carries s along with moveTo at the point of
method extraction, if not earlier in the lifetime of s.
Alex et al.'s ! proposal would let the user decide whether to bind
early or late, by letting one use . instead of ! at the point of
reference, not at the point of method definition. On the other hand
this proposal complicates the surface language (which do I use, "." or
"!"? asks the average programmer) and requires on the fly memoization.
So with current JS implementations, whether using the ES5 built-in
Function.prototype.bind or a bind progenitor from a JS library,
naively binding every method to each instance in a constructor forces
a new unjoined function object for every method times every instance.
This not only can cost too much, it may break a wanted method-identity
property that putting the method on a common prototype object preserves.
This is a case where new syntax at the method definition site can
help. Then if one does
function Sprite(i) {...}
Sprite.prototype = {
method moveTo(x,y) {...} // <- new syntax here
};
the price of allocating an unjoined function object to associate
moveTo with s will be incurred, and identity diverges, only if one
extracts the method to call later, or tries to override |this| via
apply or call.
But the common case of s.moveTo(x, y) for all one million Sprite
instances pays no such price and preserves the prototype-homed method
identity.
/be
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss