Partial Application in JavaScript using bind()
passy.svbtle.com
passy.svbtle.com
My common use case is when I'm setting up event handlers and I want the event handler to know about a companion object I have to the element in the DOM. For example:
MyObject.prototype.buildDom_ = function() {
for (var i = 0; i < buttons.length; i++) {
var button = new MyButton(...);
this.myButtons_.push(button);
var element = document.createElement('div');
element.addEventListener('click', this.handleClick_.bind(this, button));
this.rootElement_.appendChild(element);
}
};
MyObject.prototype.handleClick_ = function(button, clickEvent) {
// I have access to the MyButton instance passed in.
};
In this case, I use the partial application to pass in the MyButton instance so that when I'm in the handler for a button click, I have access to the original JavaScript object that was created when the event handler was set up.`bind` returns a new function, which has it's context `this` set to whatever you pass in, along with arguments partially applied.
`apply` calls the function, using the context object you provide and the arguments provided in an array. (`apply` also has a sister function `call`, which also calls the function with the context object you provide and the arguments you provide individually).
Both call and apply call the function it's given immediately.
That's why bind is useful in the context of event handlers--you want code to run at some later time. If you do this with 'apply', you still need a wrapper function.
Moreover, there is some manual manipulation of the `arguments` special object you need to do in order to get this to work with apply. Without bind you'd have to do something like
function tweetWithType(tweetType) {
return function() {
var newArgs = Array.prototype.slice.call(arguments);
newArgs.unshift(tweetType);
this.handleStreamEvent.apply(this, newArgs);
};
}
to be able to call it like this.on('tweet', tweetWithType('tweet'));
and you STILL wouldn't be right, because `this` within tweetWithType is not the `this` you expect. You'd have to do the whole var self = this;
thing.1. Y.rbind, which does the same thing as bind, but appends the arguments (to me, this is more useful, as often, I'm binding a single function thats defined somewhere that expects at least the event obj as the first arg, but may be able to handle optional arguments that I could bind to an instance of that fn)
2. The ability to create a bound function using only the name of the function and the object it exists on. Using something like Y.bind('myFn', obj);, the function that will be executed will be looked up at runtime rather than at the time the bound fn is created. This is extremely useful for cases where you may be using AOP or monkey patching to displace a function before it's executed. It also allows you to bind a function before it exists. Passing in a reference to the function at bind time protects against cases like that, but I find it far more useful than just binding the reference.
http://coding.smashingmagazine.com/2014/01/23/understanding-...
Function.prototype.partial = function () {
var
fn = this,
args = Array.prototype.slice.call(arguments)
;
return function() {
var newArgs = Array.prototype.slice.call(arguments);
return fn.apply(this,args.concat(newArgs));
}
}var boundFn = function(){ return originalFunction.apply( this, [boundArg1, boundArg2].concat( arguments ) ); }
or something like that.
document.addEventListener('click', handleEvent.bind(this));
Doing document.removeEventListener('click', handleEvent);
doesn't work, so how can you get rid of it? var eventHandler = handleEvent.bind(this);
document.addEventListener('click', eventHandler);
document.removeEventListener('click', eventHandler);
May seem redundant but if you are trying to remove a listener without using bind you still need a reference. var boundEvent = handleEvent.bind(this);
document.addEventListener('click', boundEvent);
and later: document.removeEventListener('click', boundEvent);
This situation is a bit messier, though, because you have to keep track of both `handleEvent` and `boundEvent`.This pattern encourages naming of anonymous functions that are used in callbacks. Which creates simpler stack traces. The new piece here about passing additional params to bind is also helpful, in that it will allow you to carry params across a callback stack. For me this will make carrying an express apps request and response objects across a callback chain much easier.
not a big deal to put conditional breakpoint in handleStreamEvent whereever it is, but in long run it just a waste of time.
this.setup = function () {
this.on('tweet', this.handleStreamEvent.bind(this, 'tweet'));
this.on('retweet', this.handleStreamEvent.bind(this, 'retweet'));
};
This pattern will be obsolote as soon as arrow function gets implemented by all major browsers: this.setup = function () {
this.on('tweet', () => this.handleStreamEvent('tweet'));
this.on('retweet', () => this.handleStreamEvent('retweet'));
}; this.setup = function () {
this.on('tweet', (e, data) => this.handleStreamEvent('tweet', e, data));
this.on('retweet', (e, data) => this.handleStreamEvent('retweet', e, data));
};