Improving Function.prototype.bind

# Andrea Giammarchi (13 years ago)

I have thought it may be interesting to receive some comment here too ... so here the short summary:

genericCallback.bind(sameObject) !== genericCallback.bind(sameObject)

quite inconvenient for listeners and leading to uncomfortable patterns ( store the bound reference somewhere and get it back later )

plus bind, at the end of a function, where the topic is the context, looks more like a yoda statement

"function with context as this object"

rather than

"object as context of this function"

So, the proposal, is a simplified Object.prototype.boundTo ( or eventually, to avoid conflicts with bind signature Object.prototype.asContextOf ) where the action is object, as context, related, and the returned function is one and one only

sameObject.boundTo(genericCallback) === sameObject.boundTo(genericCallback)

or, if you prefer

sameObject.asContextOf(genericCallback) === sameObject.asContextOf( genericCallback)

Here the whole post with better examples plus the proposed solution that would be nice to have in JS.Next webreflection.blogspot.com/2012/01/improving-functionprototypebind.html

Best , Andrea Giammarchi

# Axel Rauschmayer (13 years ago)

On Jan 5, 2012, at 14:54 , Andrea Giammarchi wrote:

Here the whole post with better examples plus the proposed solution that would be nice to have in JS.Next webreflection.blogspot.com/2012/01/improving-functionprototypebind.html

I don’t use bound() and function expressions very often (I prefer that = this for most use cases).

However, the register/unregister pitfall is indeed real. How about the following solution?

Function.prototype.attachTo = function (obj) {
    this.bound = this.bind(obj);
    return this;
}

var obj = {
    mymethod: function () {
        // ...
    }.attachTo(obj)
}

generic.addEventListener("stuff", obj.mymethod.bound, false);
generic.removeEventListener("stuff", obj.mymethod.bound, false);

Python always binds methods that you access via obj.mymethod. Multiple accesses are equal (which I think is carried over to data structures), but not the same reference.

It’s a shame that we are really close in JavaScript, with the ECMA-262 specification using references.

# Andrea Giammarchi (13 years ago)

that would not solve much 'cause you can bind a function to a single object and no more.

My proposal is object related so that eventually, even if you forget to explicitly drop already bound callbacks, those stacks should be GCed automagically and memory speaking it should be safe.

@Jake I find that a bit too much universal approach, I mean ... it does not real scale and it may be redundant in many cases, as example when you simply have the right context in a function and you call other via this where the bound one would not be necessary.

Object.prototype.bindAll could be, in any case, a nice addiction, except we need proxies for runtime added/borrowed methods when/if necessary otherwise the risk is double bound without reasons and at that time the problem about listeners would still be there.

# François REMY (13 years ago)

It’s a shame that we are really close in JavaScript, with the ECMA-262 specification using references.

As things are going, things are not going to change. The strong desire to avoid any behavior breakingchange between ES5 and ES6 actually makes it impossible to fix a lot of issues. I’m starting to think Google may be right to start over a new language, the comitee is too conservative about ES. But, I agree, it’s a shame.

# Axel Rauschmayer (13 years ago)

that would not solve much 'cause you can bind a function to a single object and no more.

Isn’t that the most frequent use case? Do you have examples where you bind the same function to several objects?

# Andrea Giammarchi (13 years ago)

all the time since I widely use private methods and I reuse them per each created instance of a specific "class"

The boundTo() approach would make the method still private, immutable out there, and reusable per each instance/object I need.

// stupid useless example ... just as concept var Counter = (function () { // private function increase() { this.age++; }

function Counter() { // none out there should be able to retrieve the bound function document.addEventListener("click", this.boundTo(increase), false); }; Counter.prototype.age = 0; Counter.prototype.destroy = function () { // so that only this class scope can control things and nobody else document.removeEventListener("click", this.boundTo(increase), false); }; return Counter; }());

Last, but not least, I do duck typing and I often borrow methods around for smaller common tasks

Best , Andrea Giammarchi

# Axel Rauschmayer (13 years ago)

As things are going, things are not going to change. The strong desire to avoid any behavior breakingchange between ES5 and ES6 actually makes it impossible to fix a lot of issues. I’m starting to think Google may be right to start over a new language, the comitee is too conservative about ES.

Don’t forget that we do get a lot of really nice features via ECMAScript.next, including fixes for most quirks. The incremental approach does make sense and keeps everyone

# David Bruant (13 years ago)

It seems that what you want can be implemented as a library [1] (actually you did it as well in your blog post). In this gist, a cache is used. In a nutshell, it is a '(function, object) -> boundFunction'

mapping. I used 2 levels of WeakMaps to achieve this.

I don't think a native implementation could be that much more efficient neither in space nor time.

Assuming my implementation does what you need, what would be the benefit of a native implementation over what I propose?

David

[1] gist.github.com/1567494

Le 05/01/2012 14:54, Andrea Giammarchi a écrit :

# Andrea Giammarchi (13 years ago)

leaks, performances, as example, are the first things I have in mind when I look at that code ( assuming I understand how WeakMap works there )

Function.prototype.bind could have been implemented via libraries ( as Prototype did ) as well so I don't get your argument, sorry.

My point is that Function.prototype.bind is used 90% of the time with context only, 10% with arguments, 0% as different object since nobody uses two bound functions to the same object, arguments a part.

I am suggesting a semantic improvement Object related but of course I can solve all missing real-nedeed things via a library ... you know what I mean?

# François REMY (13 years ago)

I have to agree. I’m just convinced that you have to break compatibility at some point, or you "die". VB is a good sample. VB was great for the time he was introduced but it has many quirks. At some point, Microsoft broke compatibility, and VB.NET is born. Some developers complained, but I’m pretty sure nobody using VB.NET today would like to go back and make a new program using VB6.

I’m not an UA implementor, so I don’t want to pretend I know what should be done. I can’t help but repeat what I said to the IE team some time ago :

<< While I agree with you that keeping the syntax identical allows higher compatibility, you should note that introducing new syntax is needed at some point to introduce new features "the right way" (EDIT) or to fix language quirks.

Web applications are coming to a point where new browsers will become a requirement, not due to impossibility of making the app using fallbacks but due to poor performance and too high maintenance and bug finding costs.

I trust Microsoft for doing the things right. ES4 was a mistake you helped to avoid. It was too much, too quickly. But syntax changes will come someday. Don't hesitate too much... or you will fragment the web scripting with tons of "hyper" languages like CoffeeScript. It isn't a good idea. Or at least it doesn't seems to me.

François

From: Axel Rauschmayer Sent: Thursday, January 05, 2012 10:24 PM To: François REMY Cc: Andrea Giammarchi ; es-discuss Subject: Re: Improving Function.prototype.bind

As things are going, things are not going to change. The strong desire to avoid any behavior breakingchange between ES5 and ES6 actually makes it impossible to fix a lot of issues. I’m starting to think Google may be right to start over a new language, the comitee is too conservative about ES.

Don’t forget that we do get a lot of really nice features via ECMAScript.next, including fixes for most quirks. The incremental approach does make sense and keeps everyone on board. A more radical approach has been tried – ECMAScript 4 – and abandoned. Furthermore, the input going into ES.next is diverse (E, Racket, Smalltalk, etc.). Lastly, progress is steady and if ES.next is finished by 2013 then that is quite impressive. Compare: five years between Java 6 – with few new features – and Java 7.

# Brendan Eich (13 years ago)

On Jan 5, 2012, at 8:08 AM, François REMY wrote:

It’s a shame that we are really close in JavaScript, with the ECMA-262 specification using references.

As things are going, things are not going to change. The strong desire to avoid any behavior breakingchange between ES5 and ES6 actually makes it impossible to fix a lot of issues. I’m starting to think Google may be right to start over a new language,

Lots of new languages these days -- a good thing.

Starting a language is one thing. Getting adoption is another matter.

the comitee is too conservative about ES.

But, I agree, it’s a shame.

What exactly would you do here? Making methods auto-bind on extraction, with memoization? Please be concrete and specific.

I do not believe (Axel) that the spec's Reference internal type is helpful as an externally-visible part of the language in any of this.

# Axel Rauschmayer (13 years ago)

I do not believe (Axel) that the spec's Reference internal type is helpful as an externally-visible part of the language in any of this.

I believe you, I know nothing about how references are actually implemented. Python works like this:

class Foo:
    def bar():
        pass

$ f = Foo()
$ f.bar
<bound method Foo.bar of <__main__.Foo instance at 0x103e57950>>

It has always surprised me that JavaScript preserved this here: (new Date().toString)() // (*) But not here: (0, new Date().toString)()

Then I found out that spec-wise, (*) is very close to Python. But if implementations don’t have references then it’s also impossible to keep them around. One could produce a bound function whenever one evaluates a reference to a method, but I don’t know if it’s worth the trouble (performance and compatibility-wise).

If you picture a method invocation obj.foo() as a single construct (and not as first evaluating obj.foo and then applying the () operator) then things do make sense – evaluating obj.foo is a different thing and simply gives you back the function you put there. And, thankfully, with strict mode, things fail fast if you forget to bind() a method.

# Andrea Giammarchi (13 years ago)

Guys, by any chance we can go back into the topic? I was not thinking about changing the meaning of "this" in a function, also Python sends explicit self context as first argument so things are slightly different in any case ....

Point is:

  • bind was one of the most needed/used Function.prototype ever, the reason ES5 adopted it out of Prototype library
  • bind is over-enginered for 90% of use cases and it easily leads to anti-patterns due inability to retrieve already bound functions/objects and the need to address somewhere everything if we want to remove, as example, a listener
  • bind, as it is, is more than fine, and developers got use to it, it does not mean that developer could understand an Object.prototype related method able to create and retrieve the unique function bound with that object as context

I am seeing stuff like this everywhere

this._boundMethod = this.method.bind(this);

and in my opinion is even ugly to read ... first of all is redundant all over the place, secondly the bound method is, in this way, easily exposed outside.

A private scope per each instance so that bound method would be a private variable makes even less sense ... plus bind, used with the single context argument, is a context matter, and a single context argument, should never be recreated unless strictly necessary but today I have still never seen the usage for a new bound function/object used as freshly new created object.

Nobody is using/creating two context bound on purpose so that

sameCallback.bind(sameObject) should always be === sameCallback.bind(sameObject)

since the scope the callback has been created won't ever change during app lifecycle, and accordingly it does not make sense at all to create two different results/functions/objects

I don't want to break bind as it is, all I am proposing is to bring Object.prototype.boundTo/asContextOf in core so that GC life is easier and performances much fasters than whatever WeakMap or proper method, to avoid leaks, could do.

sameObject.boundTo(sameCallback) === sameObject.boundTo(sameCallback)

is a simple method that could change 90% of frameworks out there and it's shimmable, through my code, or others libraries.

Any thoughts on this ? It's just needed, so if you have a better idea, since those exposed until now do not scale or are not faster/shimmable, I'd like to listen to it.

Thanks and Best

# Brendan Eich (13 years ago)

On Jan 5, 2012, at 4:47 PM, Andrea Giammarchi wrote:

Guys, by any chance we can go back into the topic?

You'll have to polyfill Function.prototype.bind in the current world of pre-ES5 browsers. Why not then go on to wrap it in a memoizing version that uses a WeakMap if available (emulated with strong keys/values array-based implementation if not)?

If you need this machinery, it's all doable. If we should evolve a Harmony bind or betterBind based on your experience and others, we can do that.

Right now the lack of memoization is not a burning issue, from what I hear. You've raised it, I think for the first time on es-discuss.

# Mark S. Miller (13 years ago)

Such a built in memoization, whether by boundTo or this enhancement to bind itself, creates a fatal ambient communications channel.

// initSES initialization, freezing all built in primordials other than

the true global

// load Alice as a confined object graph

// load Bob as a confined object graph. Alice and Bob should not be

able to communicate // Covert channels mean we might not be able to prevent them from communicating bits // But we must certainly prevent capability leaks

Alice says

    Object.boundTo(Object).foo = capabilityBobShouldntGet;

or Object.bind(Object).foo = capabilityBobShouldntGet;

Bob says

    var HAH = Object.boundTo(Object).foo;

or var HAH = Object.bind(Object).foo;

David's WeakMap approach elegantly avoid this problem, because Alice and Bob can only communicate if they already share access to this WeakMap, in which case they could already communicate anyway.

# Andrea Giammarchi (13 years ago)

most JS developers use what their library/framework provide and the bind is most of the time transparent.

Libraries developers simply use bind as a Santa Claus ES5 gift so they don't bother thinking about the pattern they are forced to use, they just use bind the way they can but again, not relying in fresh new objects per context.

My code, in my blog, is using same WeakMap concept, except is not using WeakMap ... I can hardly think of a browser that implements Harmony WeakMap but not Function.prototype.bind yet

Finally, accordingly with @Mark reply, I think it's still not clear what I am talking about ... so here the signature/spec:

Object.prototype.boundTo(callback:Function):BoundObject

The method accepts one argument only and this must be one with [[Call]] capabilities (a Function/callable, nothing else)

The BoundObject is a singleton generated with the unique combination of the current generic object that is calling the boundTo method, and the given Function argument.

The behavior of this BoundObject is exactly the same of

callback.bind(genericObject)

except it's a singleton.

A smart implementation should be able to release this singleton once the genericObject that invoked boundTo has reference counter equals to zero and of course, if nothing else is holding the BoundObject explicitly ( but in that case the reference counter of genericObject cannot be zero so ... )

If latest point is "too smart/complicated" then the signature would be

Object.prototype.boundTo(callback:Function[, erase:Boolean=false]):BoundObject

where a call to

genericObject.boundTo(callback, false)

should be able to return the BoundObject in any case but destroy it internally. Latter point is mainly for shims and I think not needed.

Accordingly with this, everything that Mark wrote in his last reply could not happen ... everything will work as expected.

Any better ?

I can try to explain even more, if necessary, or provide better specifications aligned with current ES5 terminology

Best , Andrea Giammarchi

P.S. is it really just me that see the problem with current bind ?

# Mark S. Miller (13 years ago)

In your proposal, what does this code sequence do:

Object.boundTo(Object).foo = capabilityBobShouldntGet;
var HAH = Object.boundTo(Object).foo;

?

# Andrea Giammarchi (13 years ago)

same thing this does

var bound = Object.bind(Object); bound.foo = capabilityBobShouldntGet; // who does this ?

var HAH = bound.foo;

... so I am missing your point I guess ... but my proposal wants to return always same object for 99.9% of usage out there ...

# David Bruant (13 years ago)

Le 05/01/2012 23:10, Andrea Giammarchi a écrit :

leaks

When a function has no strong reference, the associated entry in the first WeakMap (which is the second level of weakmap) can be GC'ed. When an object has no strong reference, all entries in second-level WeakMaps can be collected. I don't see any leaks. Assuming a GC with reachability, each bound function is kept only if both the function and the object are still in the environment (which is the minimum we need to achieve the functional goal).

performances

A native implementation can use a hash table using the 2 references as keys. I don't know to what extent it would be that much better.

as example, are the first things I have in mind when I look at that code ( assuming I understand how WeakMap works there )

(...)

My point is that Function.prototype.bind is used 90% of the time with context only, 10% with arguments, 0% as different object since nobody uses two bound functions to the same object, arguments a part.

And you obviously have a one-year study crawling over 100,000 websites and 1000 node projects to back these numbers?

Also, does "use" refer to the occurence of code written doing what you describe or occurence of run?

All in all, let's not use numbers or quantifiers when there is no backing besides the experience of a few, because no reliable decision can really be taken based on that.

Function.prototype.bind could have been implemented via libraries ( as Prototype did ) as well so I don't get your argument, sorry.

I am suggesting a semantic improvement Object related but of course I can solve all missing real-nedeed things via a library ... you know what I mean?

My point is that what can be solved efficiently should be by a library. I think the solution I've provided would be satifactory (you can obviously disagree).

I'm more interested in ECMAScript solving problems that either can't be solved or not efficiently. This currently includes private names, weakmaps, modules, proxies, all the syntax sugar, binary data...

# François REMY (13 years ago)

Such an implementation would be very slow and not efficient, because searching for a key in the "boundFunctions" weakmap would take time. It can be a polyfill solution but a new implementation need something better.

I think the solution would be to create an "invisible" [[boundFunctions]] property for all objects containing a dictionnary<weak<function>,

weak<boundFunction>> of already-bound functions on the object. The

implementation of bind would then be :

  • if objToBind is not a reference, create a new boundFunction and return it.
  • if objToBind.[[boundFunctions]] don't exist, create it.
  • else, check if it contains a key for functionToBind //when you are processing the dictionnary, remove obsolete entries you may find
    • if yes, check if the weak reference is alive
    • if yes, returns the boundFunction
  • create a new boundFunction and store it into objToBind.[[boundFunctions]]
  • return the newly created boundFunction

Would that be possible?

François

-----Message d'origine---

# François REMY (13 years ago)

My proposal could break existing code so it’s probably not worth a shot in ES6. Maybe in ES7, then.

From: Brendan Eich Sent: Friday, January 06, 2012 12:52 AM To: François REMY Cc: Axel Rauschmayer ; Andrea Giammarchi ; es-discuss Subject: Re: Improving Function.prototype.bind

What exactly would you do here? Making methods auto-bind on extraction, with memoization? Please be concrete and specific.

It would be to do the same thing as the old IE behavior with host functions.

var $ = document.getElementById; $(‘id’) // works as expected, on document $.call(anotherDocument, ‘id’); // works as expected, on anotherDocument setImmediate(myObj.asyncAction); // works as expected

That means that a function reference returned by obj.func would have a “default” this to be used when there’s no this available. Implementation may use an internal [[boundFunctions]] property to make sure to return the same “boundFunction” instance each time when accessed on an object. Or another trick could be used as operator overloading to make it feel like that while it’s not true (but it would need to update native functions like addEventListener as well, which is not easy/efficient).

Please note that the current behavior of using a “Reference” doesn’t need to be updated, it’s just that when the reference is flattened to a “Function” it should be flattened in a “Default-Bound Function” instead. For most of the code I see on the web, it would not make any difference.

This would not change the behavior of the bind function which makes a function whose “this” is ALWAYS the boundObject (consider document.querySelector.bind(document).call(document.body.firstChild, "body"))

François

# David Bruant (13 years ago)

Le 06/01/2012 06:03, Mark S. Miller a écrit :

Such a built in memoization, whether by boundTo or this enhancement to bind itself, creates a fatal ambient communications channel.

// initSES initialization, freezing all built in primordials other 

than the true global

// load Alice as a confined object graph

// load Bob as a confined object graph. Alice and Bob should not 

be able to communicate // Covert channels mean we might not be able to prevent them from communicating bits // But we must certainly prevent capability leaks

Alice says

    Object.boundTo(Object).foo = capabilityBobShouldntGet;

or Object.bind(Object).foo = capabilityBobShouldntGet;

Bob says

    var HAH = Object.boundTo(Object).foo;

or var HAH = Object.bind(Object).foo;

David's WeakMap approach elegantly avoid this problem, because Alice and Bob can only communicate if they already share access to this WeakMap, in which case they could already communicate anyway.

Are you talking about gist.github.com/1567494 ? Because I think I have the same issue. I think I faithfully implemented what Andrea described. Everyone with access to Object.prototype.boundTo has (implicitely) access to the WeakMap, so the capability leak you describe remains in my example, I think.

# Andrea Giammarchi (13 years ago)

if WeakMaps are so smart ... it means we cannot shim them without causing leaks in non WeakMap ready browsers since no magic will happen, objects as keys will simply be persistent in the WeakMap private scope ... does not look good, means we cannot solve this via libraries, means my initial proposal is better, leaks speaking, and it uses same WeakMap concept ( which to me is not new at all, check my Relator function if you want, it's from years ago )

About numbers, take 100% of framework out there and find one that use two bound as objects ... I believe you won't find any, what you'll find are a lot of anti patterns to hold already bound references.

This is 100% of the web? No, you just need jQuery and see what they have to do in order to solve this since almost every website out there uses a framework/library

br

# Andrea Giammarchi (13 years ago)

that's pretty much what I have already written in my blog ... isn't it?

The speed up is that "ghost" list of bound functions per object hard to properly manage, performances speaking, via pure JS

It seems that at least you perfectly got what I am talking about, yeah :D

br

# David Bruant (13 years ago)

Le 06/01/2012 10:40, Andrea Giammarchi a écrit :

if WeakMaps are so smart ... it means we cannot shim them without causing leaks in non WeakMap ready browsers since no magic will happen,objects as keys will simply be persistent in the WeakMap private scope

Indeed. Also, Mark Miller mentionned a couple of times that the SES polyfill [1] leaks less than one could expect. I haven't taken the time to look into that but it's probably worth mentionning.

...does not look good, means we cannot solve this via libraries, means my initial proposal is better, leaks speaking, and it uses same WeakMap concept ( which to me is not new at all, check my Relator function if you want, it's from years ago )

Your proposal won't be implemented in older browsers. Actually, it is very likely that your proposal would be implemented in browsers that would already have weak maps. Under these conditions. What is the benefit of a native implementation rather than an WeakMap based polyfill?

David

[1] code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js

# Andrea Giammarchi (13 years ago)

On Fri, Jan 6, 2012 at 10:50 AM, David Bruant <bruant.d at gmail.com> wrote:

Your proposal won't be implemented in older browsers. Actually, it is very likely that your proposal would be implemented in browsers that would already have weak maps. Under these conditions. What is the benefit of a native implementation rather than an WeakMap based polyfill?

David

[1] code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js

Why would you say that ? This version gist.github.com/1569978should works in every browser without problems.

I have also tested memory leaks via snapshots through the profiler ...

// snapshot, 1.2 Mb

function test(){} for (var i = 0, a = []; i < 0xFFFF; i++) a.push({}.boundTo(test));

// snapshot 35.8 Mb a = null;

// snapshot, 1.2 Mb

Andno WeakMap is used ... am I missing something ?

# David Bruant (13 years ago)

Le 06/01/2012 11:20, Andrea Giammarchi a écrit :

On Fri, Jan 6, 2012 at 10:50 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Your proposal won't be implemented in older browsers. Actually, it
is very likely that your proposal would be implemented in browsers
that would already have weak maps.
Under these conditions. What is the benefit of a native
implementation rather than an WeakMap based polyfill?

David

[1]
http://code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js

Why would you say that ? This version gist.github.com/1569978 should works in every browser without problems.

I meant that your proposal won't be implemented natively by older browsers. Sorry for the confusion.

I have also tested memory leaks via snapshots through the profiler ...

// snapshot, 1.2 Mb

function test(){} for (var i = 0, a = []; i < 0xFFFF; i++) a.push({}.boundTo(test));

// snapshot 35.8 Mb a = null;

// snapshot, 1.2 Mb

And no WeakMap is used ... am I missing something ?

In your implementation, you store in an array references to functions, not to objects.

I would guess that

var o = {}; for (var i = 0, a = []; i < 0xFFFF; i++) a.push(o.boundTo(function(){})); a = null;

leaks. Does it?

I certainly leaks, because references to the functions accumulate in 'cbStack' and are never free'd.

Having an own "boundTo" property in every single object saves you from a memory leak in the snippet you wrote above... at the cost of potential collisions (and an abstraction leak). And it would be an enumerable property in an ES3 browser. I would prefer the extra memory leak in this case, but that's a matter of taste.

# Andrea Giammarchi (13 years ago)

it doesn't ... as soon as you release the reference to o no leaks persists but of course until you keep o on hold those unique callbacks cannot be released ... but this would be true with WeakMap too, isn't it?

In any case, boundTo is suitable for listeners and the whole point is to do not hold manually those function

once again

window.addEventListener("whatever", o.boundTo(o.method), false); // later on window.removeEventListener("whatever", o.boundTo(o.method), false); // that's it

We can reuse/add the listener later on without problems but as soon as object "o" will be unreachable (no reference count === 0) everything will be fine

This, versus this anti pattern

o._leMethodBound = o.method.bind(o); window.addEventListener("whatever", o._leMethodBound, false); // later on window.removeEventListener("whatever", o._leMethodBound, false);

o._leMethodBound is exposed and used only to hold a bound method ... we have all done this until now, and to me it's kinda illogical, boring, error prone

, Andrea

# David Bruant (13 years ago)

Le 06/01/2012 12:23, Andrea Giammarchi a écrit :

it doesn't ... as soon as you release the reference to o no leaks persists but of course until you keep o on hold those unique callbacks cannot be released ...

If you're telling me that keeping only one of the object or the function alive but not both keeps the bound function alive, then you do have a leak. The bound function only has a reason to exist if both the object and the function remain in the program.

but this would be true with WeakMap too, isn't it?

No it wouldn't. Since WeakMaps keep weak references, you can't have the type of leaks I just described. As I said in [1], the bound function will be kept in memory only if both the object and the function are still strongly held somewhere (so the weakmap doesn't count).

In any case, boundTo is suitable for listeners and the whole point is to do not hold manually those function

once again

window.addEventListener("whatever", o.boundTo(o.method), false); // later on window.removeEventListener("whatever", o.boundTo(o.method), false); // that's it

We can reuse/add the listener later on without problems but as soon as object "o" will be unreachable (no reference count === 0) "Reference count === 0" and "unreachable" are different properties. An object can be unreachable even with a reference count different of 0.

everything will be fine

This, versus this anti pattern

o._leMethodBound = o.method.bind(o); window.addEventListener("whatever", o._leMethodBound, false); // later on window.removeEventListener("whatever", o._leMethodBound, false);

o._leMethodBound is exposed

only because of your implementation.

and used only to hold a bound method ... we have all done this until now, and to me it's kinda illogical, boring, error prone

I fully agree with your use case. Yet, you still haven't answered my question: If browsers implemented your proposal, it would certainly be in new versions that will certainly already have WeakMaps built-in. In this case, what would be the benefit of a native implementation rather than a library of yours?

Regarding your earlier argument about 'bind' being often implemented in libraries, it has to be noted that ES5 version of bind has features that ES3-based polyfill cannot emulate including being safe against redefinition of 'call' and 'apply'.

David

[1] esdiscuss/2012-January/019306

# Andrea Giammarchi (13 years ago)

unreachable without reference count? do you have an example different from a private scope object that is reachable from that scope so I would not call it unreachable?

In any case I understand your leaks point and if WeakMap solves this, but I strongly believe no shim will be able to emulate this behavior, no matters how much articulated the implementation is ( taking that google shim as example that also won't work as it is in older browsers in any case )

Then this is a dead line, something that you said makes sense as problem, won't be solved natively because it can be implemented via libraries ... but if this is the logic, I wonder what is ES5 about with all its extras ( Array, Function, String, etc )

# David Bruant (13 years ago)

Le 06/01/2012 12:51, Andrea Giammarchi a écrit :

unreachable without reference count? do you have an example different from a private scope object that is reachable from that scope so I would not call it unreachable?

I'm not sure I understand your question. In IE6 (7? 8?), written naively, event handlers often had a reference to the element they were bound to (via scope chain) and the element had a reference to the handler (obviously). It created a cycle that reference counting based GC was unable to collect. Mark-and-sweep GCs don't have this problem at all. It doesn't prevent every memory leak (no GC can), but is far more efficient in obvious cases than reference counting.

In any case I understand your leaks point and if WeakMap solves this, but I strongly believe no shim will be able to emulate this behavior, no matters how much articulated the implementation is ( taking that google shim as example that also won't work as it is in older browsers in any case )

Yes, in older browser, any implementation of what you want would leak in some cases.

Then this is a dead line, something that you said makes sense as problem, won't be solved natively because it can be implemented via libraries ... but if this is the logic, I wonder what is ES5 about with all its extras ( Array, Function, String, etc )

I would guess uniformisation of very frequent patterns (forEach, map, filter...), a need for a reliable isArray method that some libraries got right and other completely wrong. ES5 was far more that these shimable extras: strict mode, fine grained property control, getters/setters to name a few. There was probably also some "evangelisation" or "politics". Take Object.create. Besides the second argument, it can be shimed. Yes, but having this fundamental construct also helps a lot in understanding and teaching the language.

Also, I did not say that everything that can be done with a library should not be part of the language. I'm in priority interested in seeing in the language things that cannot be done (at all or efficiently) with a library. That's just a priority (of mine, by the way. I haven't seen an expression of TC39, but the current list of proposal speaks for itself, I think) It seems that it has been in JavaScript from the beginning that the language provides basic bricks and people can feel free to create themselves what they want/need on top of that.

Some patterns were very common and have later been integrated into the language. Some functions could probably be made far more efficient or reliable when integrated in the language.

I think that bringing in the language things that can be implemented in libraries should be justified by more than a use case. For instance, I'm enthusiastic of promises [1], these are more and more used, there are a lot of different APIs. It will be interesting to see what wins and bring this to ECMAScript when it seems promises have been "explored".

But I still don't see a strong justification in a memoized bind. I understand and agree with the use case, but it doesn't seem enough to bring it into the core language (and you haven't answered the security concern raised by Mark...)

David

[1] strawman:concurrency

# Andrea Giammarchi (13 years ago)

there is no security issue ... it's meant like that plus what Mark did not think about, is that if I use

(function () { function callback() {}

var object = {};

window.addEventListener("no way", object.boundTo(callback), false);

// later on

window.removeEventListener("no way", object.boundTo(callback), false); }());

inside a scope other objects can not reach, nobody will ever be able to retrieve the resulting bound function/object.

The security issue is indeed all over now with current way to store bound once callbacks per object.

object._boundMethod = object.method.bind(object);

Above pattern is able to destroy entire framework but nobody ever complained ... well, glad to be the first one.

br

# David Bruant (13 years ago)

Le 06/01/2012 16:54, Andrea Giammarchi a écrit :

there is no security issue ... it's meant like that plus what Mark did not think about, is that if I use

(function () { function callback() {}

var object = {};

window.addEventListener("no way", object.boundTo(callback), false);

// later on

window.removeEventListener("no way", object.boundTo(callback), false); }());

inside a scope other objects can not reach, nobody will ever be able to retrieve the resulting bound function/object.

Of course, the case you show is not a problem. The problem arise when 2 potentially malicious scripts have access to the same object (Mark used 'Object' as an example). But with modules, module loaders and the end of global scope, I wonder to what extent this happens. i'll answer directly to Mark message to discuss this.

# David Bruant (13 years ago)

Le 06/01/2012 06:03, Mark S. Miller a écrit :

Such a built in memoization, whether by boundTo or this enhancement to bind itself, creates a fatal ambient communications channel.

// initSES initialization, freezing all built in primordials other 

than the true global

// load Alice as a confined object graph

// load Bob as a confined object graph. Alice and Bob should not 

be able to communicate // Covert channels mean we might not be able to prevent them from communicating bits // But we must certainly prevent capability leaks

Alice says

    Object.boundTo(Object).foo = capabilityBobShouldntGet;

Bob says

    var HAH = Object.boundTo(Object).foo;

If Alice and Bob have been loaded as confined object graphs, they could both be exposed different objects for what each refer to as "Object", no? This can be applied to all object they could have in common (at some memory cost, obviously), I think. Maybe some problem can arise when the object is not retrieved from an identifier, but when its identity is guaranteed by the language, for instance 'Object.getPrototypeOf({})'. It can probably be worked around too, but sounds more complicated.

Since Alice and Bob don't have any reference in common by default, they wouldn't have a communication channel thanks to boundTo.

I think ES6 will help out more to prevent sharing same object at lower cost with module loaders (if I understand them properly) and proxies (let Object2 = new Proxy(Object) to create the same object with a different identity).

David

Ps: by the way, what does "HAH" mean?

# Andrea Giammarchi (13 years ago)

If the case I show is not a problem, as you see with AMD loaders and decent practices the problem does not exist with my approach while problems like this exists today everywhere for those exposed objects through namespaces or even modules.

Attaching instances properties runtime to bound functions has same impact of what Mark considered harmful and it remains like that with current patterns to trap boundOnce methods.

I believe after dunno how many years of listeners add/remove where I have in first place always attached or stored the boundOnce, where necessary, via Function#bind after or via closures and "self" as reference before, there is a reason if I am promoting this approach and I would like to see it solved properly, and as fast as possible, in core, keeping WeakMap shims whenever necessary for older browser.

I understand this is not priority for ES.Next ... just try to imagine for me the whole Class thingy is not priority at all while these solutions for such Event driven development environment as JS is are way more interesting for me.

Anyway, I showed the proposal and my concerns on current patterns, I think it's enough for you or TC guys to at least consider if it makes sense to put it there or not.

Best

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 8:31 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 06/01/2012 06:03, Mark S. Miller a écrit :

Such a built in memoization, whether by boundTo or this enhancement to bind itself, creates a fatal ambient communications channel.

// initSES initialization, freezing all built in primordials other than the true global

// load Alice as a confined object graph

// load Bob as a confined object graph. Alice and Bob should not be able to communicate // Covert channels mean we might not be able to prevent them from communicating bits // But we must certainly prevent capability leaks

Alice says

   Object.boundTo(Object).foo = capabilityBobShouldntGet;

Bob says

   var HAH = Object.boundTo(Object).foo;

If Alice and Bob have been loaded as confined object graphs, they could both be exposed different objects for what each refer to as "Object", no?

It depends on the intentions of the party that instantiated the confined Alice and Bob -- call her Carol the Confiner, or simply Carol.

If Carol wants to have a rich interaction with Alice and Bob, then she will typically want Alice, Bob, and Carol to all exist within a single SES context (frame) and thereby share the same Object. That way, none of them have to worry about the weird and complex behavior of, for example, instanceof, when computing across multiple frames.

Very concretely, Carol will want to have the same Object as Alice, and to have the same Object as Bob. Therefore, Alice and Bob will have the same Object as each other. Sharing Object and the other accessible built-in primordials (i.e., all the primordials except the global object) is safe in SES because the accessible primordial state contains no communications channels. To a first approximation, it contains no mutable state at all. The only exceptions are Date.now(), Date(), and Math.random(), none of which creates a communications channel.

[...]

Ps: by the way, what does "HAH" mean?

It is Bob's evil laugh, as Alice and Bob have successfully conspired to leak capabilityBobShouldntGet to Bob.

# Mark S. Miller (13 years ago)

Alice wouldn't normally be able to communicate 'bound' to Bob, and thus Bob wouldn't be able to read Alice's 'bound.foo'. The issue is much like ES3's mistake of evaluation a RegExp literal once to a RegExp object -- everyone executing the same code could now communicate, even if all accessible primordial state were frozen.

The answer to both is the same -- the immutable objects they share can only return mutable objects if these objects are fresh. Alice and Bob can then separately mutate them without being able to observe each other's mutations. This is why it is essential that .bind() return a fresh object each time it is called.

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 1:29 AM, François REMY <fremycompany_pub at yahoo.fr>wrote:

Such an implementation would be very slow and not efficient, because searching for a key in the "boundFunctions" weakmap would take time. It can be a polyfill solution but a new implementation need something better.

I think the solution would be to create an "invisible" [[boundFunctions]] property for all objects containing a dictionnary<weak<function>, weak<boundFunction>> of already-bound functions on the object. The implementation of bind would then be :

  • if objToBind is not a reference, create a new boundFunction and return it.
  • if objToBind.[[boundFunctions]] don't exist, create it.
  • else, check if it contains a key for functionToBind //when you are processing the dictionnary, remove obsolete entries you may find
    • if yes, check if the weak reference is alive
    • if yes, returns the boundFunction
  • create a new boundFunction and store it into objToBind.[[boundFunctions]]
  • return the newly created boundFunction

Would that be possible?

No, for the same reason. The mutable state you hung off this internal property creates an ambient communications channel.

# David Bruant (13 years ago)

Le 06/01/2012 17:43, Mark S. Miller a écrit :

On Fri, Jan 6, 2012 at 8:31 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Le 06/01/2012 06:03, Mark S. Miller a écrit :

    Such a built in memoization, whether by boundTo or this
    enhancement to bind itself, creates a fatal ambient
    communications channel.


       // initSES initialization, freezing all built in
    primordials other than the true global

       // load Alice as a confined object graph

       // load Bob as a confined object graph. Alice and Bob
    should not be able to communicate
       // Covert channels mean we might not be able to prevent
    them from communicating bits
       // But we must certainly prevent capability leaks

       Alice says

           Object.boundTo(Object).foo = capabilityBobShouldntGet;

       Bob says

           var HAH = Object.boundTo(Object).foo;

If Alice and Bob have been loaded as confined object graphs, they
could both be exposed different objects for what each refer to as
"Object", no?

It depends on the intentions of the party that instantiated the confined Alice and Bob -- call her Carol the Confiner, or simply Carol.

If Carol wants to have a rich interaction with Alice and Bob, then she will typically want Alice, Bob, and Carol to all exist within a single SES context (frame) and thereby share the same Object. That way, none of them have to worry about the weird and complex behavior of, for example, instanceof, when computing across multiple frames.

Ok. I forgot that some mecanisms relied on object identity.

Very concretely, Carol will want to have the same Object as Alice, and to have the same Object as Bob. Therefore, Alice and Bob will have the same Object as each other. Sharing Object and the other accessible built-in primordials (i.e., all the primordials except the global object) is safe in SES because the accessible primordial state contains no communications channels. To a first approximation, it contains no mutable state at all. The only exceptions are Date.now(), Date(), and Math.random(), none of which creates a communications channel.

Indeed. Mutable state does not seem to be the issue by itself. The issue would be a mutable state that can be mutated by a script (which isn't the case for Date and random)

[...]

Ps: by the way, what does "HAH" mean?

It is Bob's evil laugh, as Alice and Bob have successfully conspired to leak capabilityBobShouldntGet to Bob. :-)

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 1:31 AM, David Bruant <bruant.d at gmail.com> wrote: [...]

David's WeakMap approach elegantly avoid this problem, because Alice and

Bob can only communicate if they already share access to this WeakMap, in which case they could already communicate anyway.

Are you talking about gist.github.com/**1567494gist.github.com/1567494? Because I think I have the same issue. I think I faithfully implemented what Andrea described. Everyone with access to Object.prototype.boundTo has (implicitely) access to the WeakMap, so the capability leak you describe remains in my example, I think.

No, because that code would safely fail under SES, at the assignment

 Object.prototype.boundTo = function(fct){

since Object.prototype is frozen. I was referring to your approach. Note that I did not need to offer a specific alternative implementation and API, because any implementation and API that you design that actually provides this service under SES will require the memo state used to be separately instantiated for Alice and Bob. Thus Alice's memo cannot return something that Bob's memo memoized.

# François REMY (13 years ago)

Sorry, I don’t understand how. [[boundFunctions]] is not readable from the script itself, only from the UA.

To retreive an element from [[boundFunctions]] you need the original function used to create it. If you’ve recieved an instance to that function, you already have a communication channel, right? Or am I missing something?

From: Mark S. Miller Sent: Friday, January 06, 2012 5:53 PM To: François REMY Cc: Brendan Eich ; Andrea Giammarchi ; es-discuss Subject: Re: Improving Function.prototype.bind On Fri, Jan 6, 2012 at 1:29 AM, François REMY <fremycompany_pub at yahoo.fr> wrote:

Such an implementation would be very slow and not efficient, because searching for a key in the "boundFunctions" weakmap would take time. It can be a polyfill solution but a new implementation need something better.

I think the solution would be to create an "invisible" [[boundFunctions]] property for all objects containing a dictionnary<weak<function>, weak<boundFunction>> of already-bound functions on the object. The implementation of bind would then be :

  • if objToBind is not a reference, create a new boundFunction and return it.
  • if objToBind.[[boundFunctions]] don't exist, create it.
  • else, check if it contains a key for functionToBind //when you are processing the dictionnary, remove obsolete entries you may find
    • if yes, check if the weak reference is alive
    • if yes, returns the boundFunction
  • create a new boundFunction and store it into objToBind.[[boundFunctions]]
  • return the newly created boundFunction

Would that be possible?

No, for the same reason. The mutable state you hung off this internal property creates an ambient communications channel.

François

-----Message d'origine----- From: Brendan Eich Sent: Friday, January 06, 2012 2:22 AM To: Andrea Giammarchi Cc: Axel Rauschmayer ; François REMY ; es-discuss Subject: Re: Improving Function.prototype.bind

On Jan 5, 2012, at 4:47 PM, Andrea Giammarchi wrote:

Guys, by any chance we can go back into the topic?

You'll have to polyfill Function.prototype.bind in the current world of pre-ES5 browsers. Why not then go on to wrap it in a memoizing version that uses a WeakMap if available (emulated with strong keys/values array-based implementation if not)?

If you need this machinery, it's all doable. If we should evolve a Harmony bind or betterBind based on your experience and others, we can do that.

Right now the lack of memoization is not a burning issue, from what I hear. You've raised it, I think for the first time on es-discuss.

/be


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 1:50 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 06/01/2012 10:40, Andrea Giammarchi a écrit :

if WeakMaps are so smart ... it means we cannot shim them without causing leaks in non WeakMap ready browsers since no magic will happen,objects as keys will simply be persistent in the WeakMap private scope

Indeed. Also, Mark Miller mentionned a couple of times that the SES polyfill [1] leaks less than one could expect. I haven't taken the time to look into that but it's probably worth mentionning.

And it's probably worth looking into ;)

# Mark S. Miller (13 years ago)

Yes. Under SES, Object is shared by Alice and Bob (and Carol). But it is not a communications channel since it (and all its methods, etc) are all frozen.

# Andrea Giammarchi (13 years ago)

that whole variable can be redefined or used as communication channel ... I really don't understand what is the problem.

The returned bound once object could be frozen without problems but if the function is private with the gist I have posted:

  1. you cannot redefine Object.prototype at all
  2. you cannot access single bound function/object from any other place

If I have my own scope I want to do what's needed, this does not mean the problem does not exist, isn't it?

In your case you have, as example, different problems with that WeakMap

  1. everyone can WeakMap = function () {}; anywhere
  2. if already defined, the definition of Object getOwnPropertyDescriptor to avoid access to that "protected" variable will fail and throw an error ... isn't it? That does not look safe either
  3. as you wrote, Proxy easily discover that secret
  4. your implementation has same problem I am talking about ... trapped "protected/private" stuff others should not see

Same stuff with my problem except even proxy won't be able to retrieve that bound function since no property is attached.

I can't hardly believe you are so worried about an edge case and you don't see the pachyderm we are all dealing with ... properties attached and accessible "from everyone" ... this is for you safe?

Is obj.bound = obj.method.bind(obj) a better approach than the one I am suggesting ?

I am sorry but I don't think so.

Best

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 9:27 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

that whole variable can be redefined or used as communication channel ... I really don't understand what is the problem.

The variable "Object"? No, in SES that's unassignable. As layered on ES5, we enforce that simply by making globalObject.Object a non-writable non-configurable data property.

The returned bound once object could be frozen without problems but if the function is private with the gist I have posted:

Essentially yes. The returned bound object would have to be transitively immutable, but in this case freeze may be enough. Of course, this can't be a fix to "bind" since that would break bind's current behavior. But if it were a new API as you suggest, that would no longer violate any principles, I believe.

Separately, I do not believe the need for this is adequate to justify adding a new API. But that's a completely different topic.

  1. you cannot redefine Object.prototype at all
  2. you cannot access single bound function/object from any other place

If I have my own scope I want to do what's needed, this does not mean the problem does not exist, isn't it?

In your case you have, as example, different problems with that WeakMap

  1. everyone can WeakMap = function () {}; anywhere

Again, no they can't because all whitelisted global variables are unassignable.

  1. if already defined, the definition of Object getOwnPropertyDescriptor to avoid access to that "protected" variable will fail and throw an error ... isn't it? That does not look safe either
  2. as you wrote, Proxy easily discover that secret
  3. your implementation has same problem I am talking about ... trapped "protected/private" stuff others should not see

Same stuff with my problem except even proxy won't be able to retrieve that bound function since no property is attached.

I can't hardly believe you are so worried about an edge case and you don't see the pachyderm we are all dealing with ... properties attached and accessible "from everyone" ... this is for you safe?

Is obj.bound = obj.method.bind(obj) a better approach than the one I am suggesting ?

I am sorry but I don't think so.

I don't think the benefits you explain are worth the cost of a new API. But that's separate from the security issue.

# Andrea Giammarchi (13 years ago)

On Fri, Jan 6, 2012 at 6:56 PM, Mark S. Miller <erights at google.com> wrote:

On Fri, Jan 6, 2012 at 9:27 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

that whole variable can be redefined or used as communication channel ... I really don't understand what is the problem.

The variable "Object"? No, in SES that's unassignable. As layered on ES5, we enforce that simply by making globalObject.Object a non-writable non-configurable data property.

I was rather talking about your WeakMap

The returned bound once object could be frozen without problems but if the function is private with the gist I have posted:

Essentially yes. The returned bound object would have to be transitively immutable, but in this case freeze may be enough. Of course, this can't be a fix to "bind" since that would break bind's current behavior. But if it were a new API as you suggest, that would no longer violate any principles, I believe.

new API and frozen would just work for me

Separately, I do not believe the need for this is adequate to justify adding a new API. But that's a completely different topic.

  1. you cannot redefine Object.prototype at all
  2. you cannot access single bound function/object from any other place

If I have my own scope I want to do what's needed, this does not mean the problem does not exist, isn't it?

In your case you have, as example, different problems with that WeakMap

  1. everyone can WeakMap = function () {}; anywhere

Again, no they can't because all whitelisted global variables are unassignable.

it's a var WeakMap in the global scope ... still talking about your shim, probably I am missing some dependency?

  1. if already defined, the definition of Object getOwnPropertyDescriptor to avoid access to that "protected" variable will fail and throw an error ... isn't it? That does not look safe either
  2. as you wrote, Proxy easily discover that secret
  3. your implementation has same problem I am talking about ... trapped "protected/private" stuff others should not see

Same stuff with my problem except even proxy won't be able to retrieve that bound function since no property is attached.

I can't hardly believe you are so worried about an edge case and you don't see the pachyderm we are all dealing with ... properties attached and accessible "from everyone" ... this is for you safe?

Is obj.bound = obj.method.bind(obj) a better approach than the one I am suggesting ?

I am sorry but I don't think so.

I don't think the benefits you explain are worth the cost of a new API. But that's separate from the security issue.

Fair enough, so dead line it is.

br, andrea

# Andrea Giammarchi (13 years ago)

Maybe interesting for others too, since we talked about WeakMap a lot in this thread:

My essential polyfill here: gist.github.com/1571878

100% code coverage here: gist.github.com/1571887

Best , Andrea Giammarchi

# Mark S. Miller (13 years ago)

On Fri, Jan 6, 2012 at 10:02 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: [...]

  1. everyone can WeakMap = function () {}; anywhere

Again, no they can't because all whitelisted global variables are unassignable.

it's a var WeakMap in the global scope ... still talking about your shim, probably I am missing some dependency?

It's easy to miss. "WeakMap" appears as a whitelisted global at code.google.com/p/es-lab/source/browse/trunk/src/ses/whitelist.js#146

At code.google.com/p/es-lab/source/browse/trunk/src/ses/startSES.js#1062we place all these whitelisted globals on the sharedImports object as well as redefine the property as non-writable non-configurable on the global object.

At code.google.com/p/es-lab/source/browse/trunk/src/ses/startSES.js#1294we freeze sharedImports and all objects reachable from it by transitive prototype and reflective property traversal.

# John J Barton (13 years ago)

On Thu, Jan 5, 2012 at 11:47 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

... P.S. is it really just me that see the problem with current bind ?

On Fri, Jan 6, 2012 at 2:22 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Jan 5, 2012, at 4:47 PM, Andrea Giammarchi wrote:

Right now the lack of memoization is not a burning issue, from what I hear. You've raised it, I think for the first time on es-discuss.

/be

Since I agree with Andrea that the .bind() is a problem and since Brendan does not, I want to restate Andrea's case based on my experience.

The ingredients for this problem are exactly the ones around when bind() was invented: DOM event registration and JS methods.

The event registration 1) accepts a function, 2) requires explicit cleanup: window.addEventListener('load', boundOnLoad, false); ... window.removeEventListener('load', boundOnLoad, false); The JS method 1) must be bound to the correct object 2) be reference-able for the removeEventListener.

This combination prevents simple inline functions so commonly used by JS devs from being applied to DOM event registration, a very common use of inline functions. If you use an inline function without bind(), then it will not have the correct object binding; if you use an inline function with bind(), then it will not be reference-able for the remove. Therefore you must use a reference to a bound function as the handler and it must be a property of an object or a scope you close over. That is why our code is now littered with: baz.boundOnLoad = baz.onLoad.bind(baz); // bah, JS voodoo In my code I now have a function _bindListeners() called from initialize() where I list all of this goop. The tedium is similar to reference counting.

Note also my recent thread about anonymous methods, ie replacing "boundOnLoad" with imaginary "this.function(event) {...}"; I was prompted to float this idea because it is another approach to the above problem.

Yet another approach would mark methods as bound at declaration: this.onLoad = function(event) {...}.bind(this); This approach (and other postfix solutions) are hard to follow since the ... pushes the salient info far from the name. (Plus this is a trap, you have to be careful not to use it in literals).

I don't know if Andrea's solution is good or not, but I want to put another vote for |"this| is a problem".

jjb

# Brendan Eich (13 years ago)

On Jan 8, 2012, at 10:47 AM, John J Barton wrote:

That is why our code is now littered with:

BTW, which "our code"?

baz.boundOnLoad = baz.onLoad.bind(baz); // bah, JS voodoo In my code I now have a function _bindListeners() called from initialize() where I list all of this goop. The tedium is similar to reference counting.

The reference-counting tedium lies in the manual remove requirement. Reference counting has AddRef and Release (canonical COM names). It does not have a bind analogue. Indeed the bind requirement is more akin to requiring explicit handles or JNI-style roots for an exact GC. But we digress :-P.

Yet another approach would mark methods as bound at declaration: this.onLoad = function(event) {...}.bind(this); This approach (and other postfix solutions) are hard to follow since the ... pushes the salient info far from the name. (Plus this is a trap, you have to be careful not to use it in literals).

Another way out is |this|-free code, which is trivial to arrange with single-window, window-level event handling. For multi-window or deeper-DOM receivers, you'll need var self = this in a closure that is memoized for removal. Others on this thread say they use the var that = this; style, so it's not as if everyone must use .bind.

I don't know if Andrea's solution is good or not, but I want to put another vote for |"this| is a problem".

Mark made the case for avoiding a capability leak channel in .bind, and that's what is specified by ES5. That ship sailed. In generating a fresh function each time, it mirrored the de-facto standard based on PrototypeJS, which uses closures (also fresh for each evaluation and opaque to reflection).

David Bruant keeps pointing out how ES5 library code can be used to polyfill a memoziing bind, and Mark observes surprising lack of leaks with a strong pre-ES6 emulation of WeakMap. These do not prove there is "no problem" to solve, on the contrary they allow a solution to be developed without serializing design through TC39 and pushing for premature standardization.

When I replied that lack of memoization is not a burning issue, I was not pooh-poohing anyone's pain, simply noting that this issue (AFAICR) has not come up till now on es-discuss.

In conjunction with the library solution that will be required in the field anyway (for all browsers until some future edition is widely implementend), this says to me that developers who need memoizing bind should build and popularize it, as Sam et al. did with PrototypeJS. Rather than push for incompatible or premature changes to the standard.

If such a de-facto-standard better-bind is built, we can easily de-jure standardize it. If it isn't, that says something too: that not everyone solves the problems for which you rely on bind in the same way.

# John J Barton (13 years ago)

On Sun, Jan 8, 2012 at 11:39 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Jan 8, 2012, at 10:47 AM, John J Barton wrote:

That is why our code is now littered with:

BTW, which "our code"?

Mine, Andrea's, Hewitt's Firebug at least. I believe we have already lamented our limited ability to analyze JS code patterns statistically.

...

Yet another approach would mark methods as bound at declaration: this.onLoad = function(event) {...}.bind(this); This approach (and other postfix solutions) are hard to follow since the ... pushes the salient info far from the name. (Plus this is a trap, you have to be careful not to use it in literals).

Another way out is |this|-free code, which is trivial to arrange with single-window, window-level event handling. For multi-window or deeper-DOM receivers, you'll need var self = this in a closure that is memoized for removal. Others on this thread say they use the var that = this; style, so it's not as if everyone must use .bind.

I've used "var self" and "var that" quite a lot. It's a hack, contributes to right marching, stutters reading, but anyway the topic is bind() and how it might be better. More important, I want my reply to highlight a different meta-issue you raise: the role of libraries in testing ideas.

I don't know if Andrea's solution is good or not, but I want to put another vote for |"this| is a problem".

Mark made the case for avoiding a capability leak channel in .bind,

(Mark's case relies on believing that secure JS is a goal that trumps usability; I hope too be converted to this belief soon).

and that's what is specified by ES5. That ship sailed. In generating a fresh function each time, it mirrored the de-facto standard based on PrototypeJS, which uses closures (also fresh for each evaluation and opaque to reflection).

David Bruant keeps pointing out how ES5 library code can be used to polyfill a memoziing bind, and Mark observes surprising lack of leaks with a strong pre-ES6 emulation of WeakMap. These do not prove there is "no problem" to solve, on the contrary they allow a solution to be developed without serializing design through TC39 and pushing for premature standardization.

When I replied that lack of memoization is not a burning issue, I was not pooh-poohing anyone's pain, simply noting that this issue (AFAICR) has not come up till now on es-discuss.

In conjunction with the library solution that will be required in the field anyway (for all browsers until some future edition is widely implementend), this says to me that developers who need memoizing bind should build and popularize it, as Sam et al. did with PrototypeJS. Rather than push for incompatible or premature changes to the standard.

If such a de-facto-standard better-bind is built, we can easily de-jure standardize it. If it isn't, that says something too: that not everyone solves the problems for which you rely on bind in the same way.

Standardizing library uses make sense, except here is an example of a failure. The library version of bind() have exactly the problem we are discussing here!

Why did we end up in this (hmm) bind? Somehow the standardization process did not anticipate this issue even though it was known?

Firebug uses a library bind a lot, and we continually struggled with the memoization problem; we did not try to solve it in part because we hated Firebug's bind: it made debugging even more painful since it messes up the call stack. Using the ES bind() ends up not being a win, since I now have exactly the same problems.

jjb

# Brendan Eich (13 years ago)

On Jan 8, 2012, at 12:29 PM, John J Barton wrote:

On Sun, Jan 8, 2012 at 11:39 AM, Brendan Eich <brendan at mozilla.com> wrote: On Jan 8, 2012, at 10:47 AM, John J Barton wrote:

That is why our code is now littered with:

BTW, which "our code"?

Mine, Andrea's, Hewitt's Firebug at least.

Thanks, I see what you mean in Firebug.

I believe we have already lamented our limited ability to analyze JS code patterns statistically.

What does that have to do with betterBind? Any purely library (API) solution would be equally refractory to analysis.

I've used "var self" and "var that" quite a lot. It's a hack, contributes to right marching, stutters reading, but anyway the topic is bind() and how it might be better.

I'm not selling you on var self = this;, just pointing out the alternative used by enough people that standardizing a memoizing bind has not risen to TC39's agenda. Could be it should have, but the signal is not strong and well-tuned yet -- guess we're tuning now.

More important, I want my reply to highlight a different meta-issue you raise: the role of libraries in testing ideas.

Ok.

I don't know if Andrea's solution is good or not, but I want to put another vote for |"this| is a problem".

Mark made the case for avoiding a capability leak channel in .bind,

(Mark's case relies on believing that secure JS is a goal that trumps usability; I hope too be converted to this belief soon).

Mark could remove any memoizingBind from SES, no doubt. For ES5, we were paving a cowpath as I noted (PrototypeJS etc.).

Standardizing library uses make sense, except here is an example of a failure. The library version of bind() have exactly the problem we are discussing here!

It wasn't considered a failure by many. Where were the requests in the ES3.1/4/5 era for memoizing bind? I may have missed one, so please point it out.

Why did we end up in this (hmm) bind? Somehow the standardization process did not anticipate this issue even though it was known?

Did you read what I wrote about Prototype?

None of the bind/hitch/etc. library solutions memoize, if my memory serves. Sure, you could say lack of WeakMap or equivalent practically ensured that outcome, to avoid leaks. That's not totally clear now in light of Mark's emulated WeakMap not leaking in practice.

Anyway, this is all water under the bridge. What about the future? My position is still do the library work and popularize. Even if you really need a fast-track ES.next solution, the library work must come first.

Firebug uses a library bind a lot, and we continually struggled with the memoization problem; we did not try to solve it in part because we hated Firebug's bind: it made debugging even more painful since it messes up the call stack. Using the ES bind() ends up not being a win, since I now have exactly the same problems.

I have a question: why is bind used so much in Firebug for event listeners, when the DOM guarantees to dispatch them with the event target bound to |this|? Way back in '95, I created event handlers but did not add bind (it had to be discovered later). But dynamic |this| binding combined with DOM event target-based listening and handling kept this properly bound. What has changed?

# Mark S. Miller (13 years ago)

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com> wrote: [...]

That's not totally clear now in light of Mark's emulated WeakMap not leaking in practice.

[...]

That's a much stronger claim than I would be willing to make. My emulated WeakMaps leak much less that one would expect, and indeed less than I thought possible when I began the exercise. But the remaining extra leak is still significant for real uses.

From <code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js :

/**

  • This {@code WeakMap} emulation is observably equivalent to the
  • ES-Harmony WeakMap, but with leakier garbage collection properties.
  • <p>As with true WeakMaps, in this emulation, a key does not
  • retain maps indexed by that key and (crucially) a map does not
  • retain the keys it indexes. A map by itself also does not retain
  • the values associated with that map.
  • <p>However, the values associated with a key in some map are
  • retained so long as that key is retained and those associations are
  • not overridden. For example, when used to support membranes, all
  • values exported from a given membrane will live for the lifetime
  • they would have had in the absence of an interposed membrane. Even
  • when the membrane is revoked, all objects that would have been
  • reachable in the absence of revocation will still be reachable, as
  • far as the GC can tell, even though they will no longer be relevant
  • to ongoing computation.
# Mark S. Miller (13 years ago)

Sorry about the format mangling. Resending with simpler formatting which hopefully won't get mangled in transmission.

---------- Forwarded message ---------- From: Mark S. Miller <erights at google.com>

Date: Sun, Jan 8, 2012 at 5:05 PM Subject: Re: Improving Function.prototype.bind To: Brendan Eich <brendan at mozilla.com>

Cc: John J Barton <johnjbarton at johnjbarton.com>, es-discuss <

es-discuss at mozilla.org>

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com> wrote: [...]

That's not totally clear now in light of Mark's emulated WeakMap not leaking in practice.

[...]

That's a much stronger claim than I would be willing to make. My emulated WeakMaps leak much less that one would expect, and indeed less than I thought possible when I began the exercise. But the remaining extra leak is still significant for real uses.

From <code.google.com/p/es-lab/source/browse/trunk/src/ses/WeakMap.js :

/**

  • This {@code WeakMap} emulation is observably equivalent to the
  • ES-Harmony WeakMap, but with leakier garbage collection properties.
  • <p>As with true WeakMaps, in this emulation, a key does not
  • retain maps indexed by that key and (crucially) a map does not
  • retain the keys it indexes. A map by itself also does not retain
  • the values associated with that map.
  • <p>However, the values associated with a key in some map are
  • retained so long as that key is retained and those associations are
  • not overridden. For example, when used to support membranes, all
  • values exported from a given membrane will live for the lifetime
  • they would have had in the absence of an interposed membrane. Even
  • when the membrane is revoked, all objects that would have been
  • reachable in the absence of revocation will still be reachable, as
  • far as the GC can tell, even though they will no longer be relevant
  • to ongoing computation.
# John J Barton (13 years ago)

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com> wrote:

Anyway, this is all water under the bridge. What about the future? My position is still do the library work and popularize. Even if you really need a fast-track ES.next solution, the library work must come first.

I'm not asking for any fast-track; I think library-first makes a lot of sense. I am also asking "What about the future?".

Library work provides evidence of need and an example of progress. But every solution has trade-offs. Adopting a library solution is easy: it will have champions and evidence. Somehow we need to go around the loop (quickly) to find evidence of the downsides and more input to finding improvements. I think today ES has a lot of good contributors that can help. So a concrete suggestions: modify your stance on "library first" to ask for evidence of revision. Perhaps we can create a world where Master student projects build the pro/con cases for these smaller but critical language features. Make the goal less "standardize libraries" and more "learn/revise/retry from libraries". Some how the bind()-like functions so critical to daily use need more attention.

Firebug uses a library bind a lot, and we continually struggled with the memoization problem; we did not try to solve it in part because we hated Firebug's bind: it made debugging even more painful since it messes up the call stack. Using the ES bind() ends up not being a win, since I now have exactly the same problems.

I have a question: why is bind used so much in Firebug for event listeners, when the DOM guarantees to dispatch them with the event target bound to |this|? Way back in '95, I created event handlers but did not add bind (it had to be discovered later). But dynamic |this| binding combined with DOM event target-based listening and handling kept this properly bound. What has changed?

Sorry I don't know what you mean here.

jjb

# John-David Dalton (13 years ago)

Just a bit of back story on Function#bind because I think it's good to know. Prototype's Function#bind was based on the 2004 post "Object-Oriented Event Listening through Partial Application in JavaScript" by Daniel Brockman: web.archive.org/web/20100901172230/http://brockman.se/2004/method-references

There was no de-facto standard of Function#bind. ExtJS, PrototypeJS, MooTools, Dojo, and jQuery each had their own implementations.

MooTools Function#bind used to be implemented like: function fn() { console.log([].slice.call(arguments)); } var f = fn.bind({}, ['a', 'b']); // notice an array of arguments f('c'); // ['a', 'b'] // ignored arguments passed to the bound function

Ext's implementation is completely different: www.sencha.com/learn/legacy/Manual:Utilities:Function#createDelegate

Even though Dojo and jQuery implementations don't extend Function.prototype they follow Prototype's implementation with the exception that they also allow passing a string instead of a function and jQuery adds a guid property to the bound function. dojotoolkit.org/api/dojo.hitch, api.jquery.com/jQuery.proxy/#example-1

Prototype 1.7's Function#bind (which overwrites native bind too) isn't as ES5 compliant as it could be because its bound function doesn't behave correctly when called as part of a new expression.

function Foo(a,b,c) { this.a = a; this.b = b; this.c = c; } var Bound = Foo.bind({}, 1, 2); var b = new Bound(3); console.log(Object.keys(b)); // [], but should be ['a', 'b', 'c'] console.log(b.c) // undefined, but should be 3

# Brendan Eich (13 years ago)

On Jan 8, 2012, at 7:20 PM, John J Barton wrote:

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com> wrote:

Firebug uses a library bind a lot, and we continually struggled with the memoization problem; we did not try to solve it in part because we hated Firebug's bind: it made debugging even more painful since it messes up the call stack. Using the ES bind() ends up not being a win, since I now have exactly the same problems.

I have a question: why is bind used so much in Firebug for event listeners, when the DOM guarantees to dispatch them with the event target bound to |this|? Way back in '95, I created event handlers but did not add bind (it had to be discovered later). But dynamic |this| binding combined with DOM event target-based listening and handling kept this properly bound. What has changed?

Sorry I don't know what you mean here.

Why all the this-binding in Firebug if (as you suggested and a grep seems to confirm [but I skimmed]) the methods being this-bound are all or mostly event listeners? The DOM dispatches event handlers on the correct |this|. Why not use dynamic (unbound) |this|?

# Brendan Eich (13 years ago)

Thanks, good to survey again -- we did when considering bind for ES5 (then 3.1 IIRC). Andrew Dupont's JSConf.us 2011 talk covered the particular Prototype history too.

blip.tv/jsconf/jsconf2011

# Andrea Giammarchi (13 years ago)

I would be more than happy to go on with "libraries first" approach as long as we don't have to wait 6 years before a needed feature is going to be considered.

I have also raised the problem because it is in my opinion underestimated but mainly because I would like to avoid the fragmentation of implementations that Function#bind had indeed in its past.

My proposal was more like: would an Object.prototype.boundTo method make sense? If in this discussion list the outcome would be "Yes, it does" then me and other libraries author may already consider, at least, the proposal, and implement the best way they want with or without WeakMap shims in the meanwhile.

If the answer is "No, bind should never be objects matter" then I could come up with different proposal as long as we agree on a solution that could be considered in TC39 too.

As summary, I am trying to find a "direction" able to solve this problem. Even an interface, as common implementation independent direction, would be fine ... something based on handleEvent property, another pattern rarely seen out there, but with clear idea of how the whole problem would be solved ... example:

// does not have to be in the global Object, just saying Object.prototype.handleEvent = function handleEvent(evt) { // if there is some standard/agreed way to invoke meant method ... // let's use this pattern ... this"onEvent" + evt.type.slice(0, 1).toUpperCase() + evt.type.slice(1); };

// let's say that if the object is an object // no need to implement handleEvent // because everything is handled automagically // behind the scene ... window.addEventListener("resize", { onEventResize: function (evt) { window.removeEventListener(evt.type, this, false); } }, false);

So that if handleEvent is not explicit, the onEvent + event.type will be fired ... I am not sure it's clear what I mean ... but this would be another proposal I'd like to hear feedbacks about

br

# David Bruant (13 years ago)

Le 09/01/2012 06:29, Brendan Eich a écrit :

On Jan 8, 2012, at 7:20 PM, John J Barton wrote:

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:

Firebug uses a library bind a lot, and we continually struggled
with the memoization problem; we did not try to solve it in part
because we hated Firebug's bind: it made debugging even more
painful since it messes up the call stack. Using the ES bind()
ends up not being a win, since I now have exactly the same problems.
I have a question: why is bind used so much in Firebug for event
listeners, when the DOM guarantees to dispatch them with the
event target bound to |this|? Way back in '95, I created event
handlers but did not add bind (it had to be discovered later).
But dynamic |this| binding combined with DOM event target-based
listening and handling kept this properly bound. What has changed?

Sorry I don't know what you mean here.

Why all the this-binding in Firebug if (as you suggested and a grep seems to confirm [but I skimmed]) the methods being this-bound are all or mostly event listeners? The DOM dispatches event handlers on the correct |this|.

Relevant related documentation: developer.mozilla.org/en/JavaScript/Reference/Operators/this#As_a_DOM_event_handler

# Andrea Giammarchi (13 years ago)

Bear in mind e.currentTarget may be undefined .... e.g. in an about:blank page

document.addEventListener("click", function (e) {console.log(e.currentTarget)}, false);

would be null

# Andrea Giammarchi (13 years ago)

Just to make the second proposal easier to understand:

// abstract proto var AbstractEventHandler = { // invoked when events are fired handleEvent: function (e) { var events = this.events, type = e.type ; if (events.hasOwnProperty(type)) { events[type].call(this, e); } }, // shortcut to drop events cancelEvent: function (e) { (e.currentTarget || document).removeEventListener( e.type, this, e.eventPhase !== e.BUBBLING_PHASE ); } };

// generic "class" function WhateverUIClass(){} // extends AbstractEventHandler WhateverUIClass.prototype = Object.create( WhateverUIClass.prototype, { // extends AbstractEventHandler handleEvent: {value: AbstractEventHandler.handleEvent}, cancelEvent: {value: AbstractEventHandler.cancelEvent},

// implements events
events: {value: {
  "click": function (e) {
    console.log(this instanceof WhateverUIClass, e.type, e.eventPhase);
    // to remove the event, if necessary
    this.cancelEvent(e);
  }
}}

} );

document.addEventListener("click", new WhateverUIClass, false); document.addEventListener("click", new WhateverUIClass, true);

Best

# David Bruant (13 years ago)

Le 08/01/2012 19:47, John J Barton a écrit :

(...) window.addEventListener('load', boundOnLoad, false); ... window.removeEventListener('load', boundOnLoad, false); The JS method 1) must be bound to the correct object 2) be reference-able for the removeEventListener.

This combination prevents simple inline functions so commonly used by JS devs from being applied to DOM event registration, a very common use of inline functions. If you use an inline function without bind(), then it will not have the correct object binding; if you use an inline function with bind(), then it will not be reference-able for the remove. (...)

If you want to use inline (anonymous) functions, you always loose the ability to reference the function for a later remove. This is independent of bind. Inline functions are not compatible with remove.

# Andrea Giammarchi (13 years ago)

indeed ... inline functions are problem number 2, anonymous or not, unless the removeEventListener is not performed inside the function itself so that at least the function name could be reused while if anonymous and "use strict" is in place there's no way to know which function is it.

However, two inline functions cannot possibly be the same object so, as problem itself, I would not even consider to solve this.

br

# Andrea Giammarchi (13 years ago)

sorry, wrong debug, currentTarget is always the expected one ... my bad

# Brendan Eich (13 years ago)

On Jan 9, 2012, at 12:29 AM, David Bruant wrote:

Le 09/01/2012 06:29, Brendan Eich a écrit :

On Jan 8, 2012, at 7:20 PM, John J Barton wrote:

On Sun, Jan 8, 2012 at 3:35 PM, Brendan Eich <brendan at mozilla.com> wrote:

Firebug uses a library bind a lot, and we continually struggled with the memoization problem; we did not try to solve it in part because we hated Firebug's bind: it made debugging even more painful since it messes up the call stack. Using the ES bind() ends up not being a win, since I now have exactly the same problems.

I have a question: why is bind used so much in Firebug for event listeners, when the DOM guarantees to dispatch them with the event target bound to |this|? Way back in '95, I created event handlers but did not add bind (it had to be discovered later). But dynamic |this| binding combined with DOM event target-based listening and handling kept this properly bound. What has changed?

Sorry I don't know what you mean here.

Why all the this-binding in Firebug if (as you suggested and a grep seems to confirm [but I skimmed]) the methods being this-bound are all or mostly event listeners? The DOM dispatches event handlers on the correct |this|. Relevant related documentation: developer.mozilla.org/en/JavaScript/Reference/Operators/this#As_a_DOM_event_handler

Do you mean this "(some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener)." part? Ghastly but not browsers Firebug targets (namely, Firefox), right?

# Andrea Giammarchi (13 years ago)

just to make it more concrete for those interested: webreflection.blogspot.com/2012/01/introducing-objecthandler.html

IDL Like description and code ... to me this is a much faster/easier/better solution than Function#bind ... unshimmable for IE < 9 but ... you know, some wrapper could do the trick in any case.

/**

  • interface ObjectHandler implements EventListener {
  •  void                handleEvent(in Event evt);
    
  •  void                remitEvent(in Event evt);
    
  •  attribute   Object  events;
    
  • };
  • @link www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventListener */ var ObjectHandler = { handleEvent: function handleEvent(e) { var events = this.events, type = e.type ; if (events.hasOwnProperty(type)) { events[type].call(this, e); } }, remitEvent: function cancelEvent(e) { e.currentTarget.removeEventListener( e.type, this, e.eventPhase === e.CAPTURING_PHASE ); } };

Best