Retaining Context for ‘This’ in Callbacks

Passing functions as callbacks to other functions can lead to complications when the keyword ‘this’ is involved. Fortunately, there are ways in Javascript to set the correct context of ‘this’. The following code has no use in a real application, but will be used for illustrative purposes.

var obj = {
a: 10,
fnLog: function(){ console.log( this.a ); },
fnInvoke: function() { invoker( this.fnLog ); }
};
var invoker = function( callback ) {
callback();
};
obj.fnInvoke(); // → logs undefined

As demonstrated in the previous post, this will yield undefined. The fnInvoke method of obj invokes the global invoker function, which in turn invokes the fnLog method. However, when the callback is invoked by the invoker function, there is no longer a context for ‘this’ and so the global window object is bound and undefined is logged, because there is not a global variable ‘a’ defined.

There are a few different ways in Javascript to get around this problem and ensure that obj is bound do ‘this’ when the fnLog method is invoked as a callback in the invoker function.

The first, and my personal favorite, is to use the .bind method available to all functions. This method, when invoked on a function, will return a new uninvoked function with the first argument passed in bound to ‘this’. When the callback is invoked now in the example, it will run the fnLog method will the correct context for ‘this’.

obj.fnInvoke = function() {
invoker( this.fnLog.bind( this ) );
};
obj.fnInvoke(); // → logs 10

Another way to set the context of ‘this’ is to save the current value of ‘this’ to a variable within the fnInvoke method and pass an anonymous function to invoker. When the callback is invoked now in the example, it will in turn invoke context.fnLog(). Since context was defined in the scope of the anonymous function, it retains access to the original object and follows the left of the dot principal for binding ‘this’.

obj.fnInvoke = function() {
var context = this;
invoker( function() {
context.fnLog();
});
};
obj.fnInvoke(); // → logs 10

A third way to set the context of ‘this’ is possible with ES6. In ES6, arrow functions implicitly bind the current context of ‘this’ for you. So by replacing the anonymous function in the previous example with an arrow function, the callback will retain the original context of ‘this’ without explicitly having to save it to a variable.

obj.fnInvoke = function() {
invoker( () => {
this.fnLog();
});
};
obj.fnInvoke(); // → logs 10

I hope this helps identify and correct any problems encountered with ‘this’ and callbacks.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.