I have always struggled to correctly use the keyword, this
, in JavaScript. This is especially the case when functions are invoked from the context of other functions. At a high level, think of this
being a variable that simply refers to an object. When this
is bound to an object, you can use this
to set or look up properties and invoke methods on such object.
Binding Patterns for this
this
can only be bound by one of five patterns, and more importantly, this
is bound to an object at call time. So how do you determine which pattern was used during call time?
- Run the code in the debugger and pause at the line that refers to
this
- Scan outward looking for the closest enclosing function body
- Once you find the function
this
appears in, examine the call stack and look one level down to where this function was called - Identify the syntax of how the function was called to determine the run pattern
Let's take a look at each pattern.
Run Pattern | Example | Binding Target | Purpose |
---|---|---|---|
Global reference | this (not enclosed in any function) |
Global object (in a browser, it is window ) |
Not useful, but JavaScript has to bind it to something to be consistent |
Free function invocation | functionName() |
Global object (in a browser, it is window ) |
Not useful, but JavaScript had to bind it to something to be consistent with the other patterns |
ABC (.apply, .bind, .call) | fn.call(target) or obj.methodName(target) |
The first argument to .apply, .bid, or .call | To manually pass in a this binding, like passing in arguments when invoking a function or method |
Method invocation | target.methodName() |
Object on the left of the call time dot | Execute the method in the context of an object they're found on |
Construction mode | new functionName() |
A new object created for that invocation | Allow a constructor to operate on the instance it is creating |
Tip:
Most of the time, when inside a function,
this
will be bound to the object that was to the left of the call time dot.
Global Reference
console.log(this); // logs window from the browser
Consider the above code that runs in the global scope. It is not enclosed in a function. Since this
is automatically bound while inside a function body, JavaScript wanted to be consistent in having this
be bound to something, so when outside a function body, this
will be bound to the global context, which in the case of a browser, is window
.
Free Function Invocation
var fn = function() {
console.log(this);
};
fn(); // logs window from the browser
fn
is being invoked in the global context. Like before, this
will be bound to the global context because that is the context during run time when fn
is invoked.
ABC (.apply, .bind, .call)
var fn = function() {
console.log(this);
};
var obj1 = {};
var obj2 = {};
fn.apply(obj); // logs obj1
fn2 = fn.bind();
fn2(); // logs obj2
fn.call(obj); // logs obj1
In this scenario, when using the native apply
, bind
, and call
Function methods, you can manually pass in an object, and this
will be bound to that object. This allows you to predictively make use of this
.
Method Invocation
var fn = function() {
console.log(this);
};
var obj1 = {mtd: fn};
var obj2 = {mtd: obj1.mtd};
obj1.mtd(); // logs obj1
obj2.mtd(); // logs obj1
Since fn
is being invoked as a method whatever object is on the left of the call time dot will be bound to this
. With obj2.mtd
, it refers to obj1
's mtd
method, and invoking obj.mtd()
would actually invoke fn
as a method of obj1
since obj1
is to the left of the call time dot.
The phrase, call time dot, is important. Consider the following:
var obj1 = {mtd: fn};
obj1.mtd(); // logs obj1
window.setTimeout(obj1.mtd, 1000); // logs window from the browser
/*
window.setTimeout() can be imagined like this:
var setTimeout = function(callback, delay) {
wait(delay); // some logic to wait a certain amount of time
callback();
};
*/
With setTimeout()
, it takes in a function and delay (in milliseconds), and it invokes the function after the delay. In this case, since fn
(the callback function that is passed as the first argument into setTimeout()
) is invoked as a free function invocation, this
is bound to the global context, window
.
Construction Mode
var fn = function() {
console.log(this);
};
var obj = {mtd: fn};
obj.mtd(); // logs obj
var obj1 = new fn(); // logs a new object
var obj2 = new obj.mtd(); // logs a new object
A function invoked with the new
keyword means the function is considered a constructor function. This expression will create a new object that is an instance of the constructor function, and the function will execute in the context of that newly-created object.
In addition, the new
keyword will override the method invocation pattern. In the example, new obj.mtd()
would not bind this
to obj
even though obj
is to the left of the call time dot. Instead, this
is bound to the newly-created object from the constructor function.
Summary
I hope this helped gives clarity on how this
is bound. Remember, most of the time, when inside a function, this
will be bound to the object that was to the left of the call time dot.