Wrapping a JavaScript callback redefining "this" - javascript

I know about call and apply in JavaScript, but I'm currently stuck with the following scenario.
I'm using Benchmarkjs, and it has an API defined like this:
.on(eventType, listener)
So I'd do, for instance:
/* Register the onStart callback */
suite.on('start', onStart);
My onStart function will be called so that this === suite.
How can I do so that I can define one of the arguments of onStart?
My code is something like this:
foo = function() {
this.suite.on('start', this.onStart);
}
I'd like my onStart function to have a reference to foo.
Any suggestions?

You can invoke Function.prototype.bind for creating partial applications / curried functions.
.bind() takes the context as first parameter and all following passed in parameters will be used as formal parameters for the bound function invocation.
suite.on( 'start', this.onStart.bind( this, foo ) );
The reference to foo will now be available to onStart() as very first argument.

You can pass this to onStart by wrapping it in a function.
foo = function() {
this.suite.on('start', function(arguments){onStart(arguments, this)});
}
I prefer passing it instead of using bind, since bind isn't supported by IE 8 and lower.

Related

Mixing jQuery, prototype and 'this'

I am working on a project that uses some JS prototyping and jQuery.
My issue, I believe, is how 'this' is being used.
In my code, I have some jQuery in a function that I will be prototyping. The jQuery looks like this:(in this code, 'target' is a jQuery object passed when IMAGE_UPLOADER is first created.)
document.getElementById(target.find('.file_selector').prop('id')).addEventListener("change", this.FileSelectHandler, false);
In this event listener, there is a function called FileSelectHandler. This function is being called just fine. However, within this function, there is a call to a second function. Here is a short version of the function:
FILE_UPLOADER.prototype.FileSelectHandler = function(e) {
this.FileDragHover(e);
}
This is where the error comes up. JS is complaining that the function 'FileDragHover' does not exist. It, of course DOES exist and is defined as follows:
FILE_UPLOADER.prototype.FileDragHover = function(e) {}
I hope this is enough info to understand the problem. If not, please let me know and I can add more.
You can use $.proxy() to pass the custom execution handler to the event handler, this inside the event handler refers to the dom element where the listener is attached - it is the same as Function.bind() but jQuery version supports IE < 9.
document.getElementById(target.find('.file_selector').prop('id')).addEventListener("change", $.proxy(this.FileSelectHandler, this), false);
The handler registration can be simplified using jQuery to
target.find('.file_selector').change($.proxy(this.FileSelectHandler, this))
If an object obj has a function func and you do obj.func() the function is called in context of obj where this in that function then referes to obj.
If you however do something like this:
var callback = obj.func;
callback();
Then callback is not called in the context of obj anymore. For browsers the context is then window. This is what is happening if you pass the function as callback to the addEventListener.
Depending how your callback is used it can be called with another context, for event listeners it is the DOM element, so this in your code refers to the DOM element.
To solve this problem you can used Function.prototype.bind (you need to check the browser support if it is usable for you or use a polyfill for it, that can be found on the mdn page), you can use jQuery.proxy or create a closure and maintain the context yourself.
The this variable refers to the invoking object not the object that the method is declared on. So if I have someObject.someFunction(); then someObject is the invoking object and the value of this in the function.
To demonstrate:
var obj1 = {
name:"obj1",
say:function(){
console.log(this.name);
}
};
var obj2 = {name:"obj2"};
obj2.say = obj1.say;
obj2.say()//logs obj2
In the above code say was declared on obj1 but invoked from obj2.
More on the this value, constructor functions and prototype here.

.call() / .apply() with NO parameters VS simply calling a function with () parenthesis

I've seen it done differently in code out there, but is there any benefit or reason to doing a (blank params) .call / .apply over a regular () function execution.
This of course is an over-simplified example
var func = function () { /* do whatever */ };
func.call();
func.apply();
VERSUS just the simple parenthesis.
func();
Haven't seen any information on this anywhere, I know why call/apply are used when params are passed.
When you call a method with func();, this variable inside the method points to window object.
Where as when you use call(...)/apply(...) the first parameter passed to the method call becomes this inside the method. If you are not passing any arguments/pass null or undefined then this will become global object in non strict mode.
Yes, there is an important difference in some cases. For example, dealing with callbacks. Let's assume you have a class with constructor that accepts callback parameter and stores it under this.callback. Later, when this callback is invoked via this.callback(), suddenly the foreign code gets the reference to your object via this. It is not always desirable, and it is better/safer to use this.callback.call() in such case.
That way, the foreign code will get undefined as this and won't try to modify your object by accident. And properly written callback won't be affected by this usage of call() anyways, since they would supply the callback defined as an arrow function or as bound function (via .bind()). In both such cases, the callback will have its own, proper this, unaffected by call(), since arrow and bound functions just ignore the value, set by apply()/call().

Avoid EVAL and pass THIS to function?

I've built a GUI which passes in a long JS Object as settings for an animation plugin. One of the settings allows for the user to call a function once an animation is complete. Unfortunately, it has to be passed to the JS Object as a string.
... [ 'functioncall()' ] ......
Inside my animation callback, I'm retrieving this string and trying to run it as a function.
First attempt works perfectly, but uses eval...
eval( OS.path_jscall[index].path_jscall[0][i] )
I've setup a more preferred approach like so:
var HookFunction=new Function( OS.path_jscall[index].path_jscall[0][i] );
HookFunction();
Both examples call the functioncall() function perfectly. I'm not sure how to pass (this) to the functioncall() though...
functioncall(obj){ console.log(obj); };
Everything keeps referring to the window. Any ideas? Thanks!
Assuming that HookFunction is the name, you can do either a call() or apply()
HookFunction.call(this,arg1,arg2,...,argN);
//or
HookFunction.apply(this,[arg1,arg2,...,argN]);
the basic difference of the 2 is that call() receives your "this" and an enumerated list of arguments after it, while apply() receives your "this" and an array of arguments
Use .call when calling your function. .call assigns the first parameter to the this variable.
var myFunction = function(arg1, arg2) {
alert(this);
}
myFunction.call(this, "arg1", "arg2");
Using your second example, you could do this:
HookFunction.call(this);

Passing method parameters/arguments to method reference - bind, anonymous, named

I have multiple places in my code where i use method references(i.e. just the method name with no arguments) but I need to pass it specefic arguments.
I don't want to insert an anonymous method b.c. it makes the code unreadable.
I've told I can use the .bind method, but I don't know how to use it properly. Can some one elaborate on how to do this.
Here is one example of where I need to to to this.
How do I use bind to add in parameters to ajax_signin?
if(d===0){ajax('arche_model.php',serialize(c)+'&a=signin',ajax_signin,b);}
If you want ajax_signin() to get called with parameters, then you have to make a separate function that you can pass to ajax that calls ajax_signin() with the appropriate parameters. There are a couple ways to do this:
Using an anonymous function:
if(d===0){ajax('arche_model.php',serialize(c)+'&a=signin',function() {ajax_signin("parm1","parm2")},b);}
Creating your own named function:
function mySignIn() {
ajax_signin("parm1","parm2");
}
if(d===0){ajax('arche_model.php',serialize(c)+'&a=signin',mySignIn,b);}
If you want to use .bind() and you are sure you are only running in browsers that support .bind() or you have a shim to make .bind() always work, then you can do something like this:
if(d===0){ajax('arche_model.php',serialize(c)+'&a=signin',ajax_signin.bind(this, "parm1","parm2"),b);}
The .bind() call creates a new function that always has a specific this ptr and always has "parm1" and "parm2" as it's first two parameters.
You should be using partial function application! IE: The following:
// This will call a function using a reference with predefined arguments.
function partial(func, context /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(context ? context : this, allArguments);
};
}
The first argument is the function you want to call, the second argument the context, and any arguments after that will be 'pre-loaded' into the function call.
Note: 'Context' is what this will refer to once the function is being executed.

Issues with executing setTimeout on a function - passing this as a parameter

Hi guys I have a function which accepts this as a parameter - 'this' referring to the dom element which upon clicked should run a function. The thing is that I want this function to be called after a small delay however passing the variable term this doesn't work as when the function is executed 'this' then doesn't refer to the object in passed in the parameter but to the window object.
How can I get this done?
You could capture this:
var t = this;
window.setTimeout(function() {
// use the t variable here
}, 2000);
PrototypeJS adds the bind() method to Function.prototype. This method allows you to bind a function and arguments to the context of a particular object. Simply,
window.setTimeout((function() {
alert(this);
}).bind(this), 2000);
The best part is that this method is now part of the ECMA-262 specification, which JavaScript is based upon, and native implementations are rolling out into modern browsers. PrototypeJS will only add this method if it's not already implemented.
I've set up an example script at http://jsfiddle.net/rLpbx/.

Categories