
What the hell is this?
this is one of those concepts in JavaScript that at first glance seems quite straightforward conceptually. But I’ve been bitten once too many times by bugs borne out of my shaky grasp of this. Here is a common definition I have come across a lot:
thisis the current execution context of a function.
This definition, even though I’ve used it a lot myself, has never quite sat well with me.
The execution context of a function is the current environment/scope the function is being evaluated in. David Sharrif wrote an excellent article on the execution context. Whenever a function is invoked, an execution context is created. This execution context contains the following: location in code where the function was called from(call-site), the parameters passed to the function, how the function was called, and among other properties, the this reference.
So, what is this? I like Kyle Simpsons’ description a lot.
thisis actually a binding that is made when a function is invoked, and what it references is determined entirely by the call-site where the function is called.
The binding mentioned in the excerpt above is to the object that “owns” the method getting called. In NEARLY every case, this stand alone function invocations.
It’s important to note that binding to this occurs at runtime, not at author-time(when you declare the function in your code).
There are 4 rules that determine what object this binds to, and each of them depends on how the function is invoked.
- Plain Function Invocation
function foo() {
console.log(this);
}foo(); // global objectfunction foo() {
"use strict";
console.log(this);
}foo(); // undefined
With a function invocation this binds to and references the global object. Depending on your runtime environment, this can be the window object(in browsers) or object in node. The second version of foo is running in strict-mode, and in strict-mode the global object is not eligible for this binding. Hence this is assigned the value undefined.
As a side note, when used in the global scope, this also references the global object.
if (this === window) {
console.log(true);
}2. Method Invocation
A method is an object property that is a function. If you’re not very familiar with objects in JavaScript, please read it up here.
var person = {
talk: function() {
console.log('Hello.');
}
};person.talk(); // Hello.
A property accessor allows us to access properties of an object. This is often written with the dot notation: an object followed by a period and then the property being queried. A method invocation is an expression that takes the form of an object with a property accessor for a method followed by a pair of parentheses: person.talk();
In the case of method invocations, this binds to the object that the method belongs to. Using the example above, the object referenced by the variable person has a method talk. Hence, when talk is invoked this binds to person object. This is an implicit binding that occurs with object method.
In the code below, the talk method is modified to log the value of this to the console. It’s output is the object the method belong to.
var person = {
talk: function() {
console.log(this);
}
};person.talk(); // Object { "talk": [function talk] }
A pitfall to be aware of is the loss of this binding that occurs when a method is reassigned to a new variable. Let me illustrate this:
var person = {
talk: function() {
console.log(this);
}
};var a = person.talk;a(); // global object
The talk function now referenced by a is no longer bound to the person object. In non-strict mode, this now binds to the global object and in strict-mode is assigned the value undefined .
3. Explicit Binding
With explicit binding you can make a function use a particular object as its this reference. JavaScript provides 3 methods just for that:
a. Function.prototype.call
b. Function.prototype.apply
c. Function.prototype.bind
var obj = {a: 1};function foo() {
console.log(this.a);
}foo.call(obj); // 1
The call() method calls a function with the first argument being the object that this should bind to, and required arguments passed in individually.
var obj = {num: 1};function foo(x,y,z) { console.log(this.num, a, b, c);}foo.call(obj, 'a', 'b', 'c'); // 1, a, b, c
The apply() method is almost similar to call() except it takes an arguments list as the second argument.
var obj = {num: 1};function foo(x,y,z) { console.log(this.num, a, b, c);}foo.apply(obj, ['a', 'b', 'c']); // 1, a, b, c
The bind() method just like call() and apply() takes an object as reference for this. Unlike the previous 2 methods, it returns a function that when invoked has this bound to the object passed in to bind() .
The returned function is a bound function that acts a wrapper for the function it was called on(foo). In essence, whenever the returned function is called, it invokes foo() .
var person = {name: 'Mike'};function bar() {
console.log(this.name);
}var bf = bar.bind(person);bf(); // Mike
It also accepts any other required arguments. They are passed in sequentially after the first argument that sets the reference for this.
var person = {name: 'Mike'};function bar(greeting) { console.log(greeting + ', ' + this.name + '.');}var bf = bar.bind(person, 'Hello');bf(); // Hello, Mike.
For more information on these methods, please check here.
4. Constructor invocation
In classic object-oriented languages, the new keyword invokes a constructor function inside a class to create an instance of that class. JavaScript is a prototype-based language, hence its implementation of new works in a different way. Nevertheless, using new to invoke a function creates an object “instance” with the constructor function set as its prototype. A constructor, in JavaScript, is just a regular function object.
The convention is to capitalize the first letter of the identifier for functions designated as constructors: function Person() {}
When a constructor is called, this binds to its instance objects. In the code below this is a stand-in for the new object instance, and in this particular case, a property name is attached and assigned the value of the argument passed in.
function Person(name) {
this.name = name;
}var person = new Person('Jim');console.log(person); // Person {name: "Jim"}
The new keyword is syntactical sugar, and behind the scenes, this is how it binds this to instance objects.
function Person(name) {
this.name = name;
}var person = Object.create(Person);Person.call(person, 'Mike');console.log(person); // Person {name: 'Mike'}
