‘this’ in Global Context:
‘this‘ in a function call:
- In most cases, the value of a function’s ‘this’ argument is determined by how the function is called. This means the ‘this’ value may be different each time a function is executed.
- In “use strict” mode, ‘this’ is defaulted to undefined and prevents us from assigning values to undefined, and accidentally creating global variables.
‘this’ in a constructor call:
- When a function is called as a constructor (with a ‘new’ keyword), a new object is created and set as the function’s ‘this’ argument.
function Person (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const person = new Person('John', 'Doe');
console.log(person) // { firstName: 'John', lastName: 'Doe' }
‘this’ in method call:
- When a function is called as a method of an object, that function’s
this
argument is set to the object the method is called on. That object is called the receiver of the function call.
- Oftentimes, the receiver gets lost when we invoke a method as a function. This happens particularly often when passing a method as a callback to another function. (example below – ‘this’ context is lost when assigning method to greet)
const person = {
firstName: 'John',
sayHi() {
console.log(`Hi, my name is ${this.firstName}!`);
}
};
const greet = person.sayHi;
greet(); // Hi, my name is undefined!
- Use a wrapper function or .bind method to workaround this problem.
Specify ‘this’ using .call() or .apply():
- Use .call and .apply to call a function with an explicit ‘this’ argument
- .call accepts arguments as a comma separated list
- .apply accepts arguments as an array.
const numbers = [10, 20, 30, 40, 50]
const slice1 = numbers.slice(1, 4); //syntactic sugar for the following:
const slice2 = numbers.slice.call(numbers, 1, 4);
const slice3 = numbers.slice.apply(numbers, [1, 4]);
// slice1, slice2, slice3 all return [20, 30, 40]
Hard-bind a function’s ‘this’ using .bind():
- permanently tie a function’s ‘this’ argument to a specific value. It creates a new bound function that calls the original function with the provided ‘this’ argument, no matter how that bound function is called.
const person = {
firstName: 'John',
sayHi() {
console.log(`Hi, may name is ${this.firstName}!`);
}
};
const greet = person.sayHi.bind(person);
greet(); // Hi, my name is John!
const otherPerson = {
firstName: 'Jane'
};
greet.call(otherPerson); // Hi, my name is John!
‘this’ can be captured with an Arrow Function (ES6):
- An arrow function doesn’t have its own ‘this’. Instead, it uses the ‘this’ value from its enclosing execution context.
- When an arrow function is created, it permanently captures the surrounding ‘this’ value. Neither .call() nor .apply() can change the ‘this’ value later.
- Arrow functions cannot be used as constructors.
- Solves issues of incorrect ‘this’ values within callback functions.
const counter = {
count: 0,
incrementPeriodically() {
setInterval(() => {
console.log(this.count++);
}, 1000);
}
};
counter.incrementPeriodically();
// 'this' stays bound to counter object inside setInterval.
‘this’ in classes:
- Within the constructor of a class, this refers to the newly created object. Within a method, however, this might refer to another value if the method is called as an ordinary function. Just like any other object method, class methods can lose their intended receiver this way.
class Person {
constructor (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.sayHi = this.sayHi.bind(this);
}
sayHi() {
console.log(`Hi, my name is ${this.firstName}!`);
}
}
const person = new Person('John', 'Doe');
const greet = person.sayHi;
greet(); // Hi, my name is John
// ('this' is bound to object in constructor)
- You can also bind this using class fields with arrow functions:
class Person {
// class field with arrow function
sayHi = () => {
console.log(`Hi, my name is ${this.firstName}!`);
}
constructor (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
const person = new Person('John', 'Doe');
const greet = person.sayHi;
greet(); // Hi, my name is John
// ('this' is bound to instance in class fields w/ arrow functions)
*Use a transpiler like Babel to translate ES6 into ES5 if using class fields and arrow functions.