In JavaScript, this
is a property of the execution context in which a function is executed.
The explanation for how this
gets evaluated is very elaborate, and it is covered case by case in this MDN article comprehensively.
The value of a function's this
is determined mostly by how the function is being called in the call site, rather than how the function is defined: things like whether strict mode is enabled or not, whether the function is defined and called standalone or not, whether we are calling the function as a method of an object or if we are extracting a reference of an object method and then calling it somewhere else, etc.
Execution Context
A function's execution context is the environment in which the function is executed at runtime. It includes, the variable scope, function arguments and the value of this
object.
this
If we need a function which acts on the properties of an object we want to use, its this
ought to be that object. In other words, our function's target object has to be made available to the execution context at runtime, so that we are able to access it with this
.
In normal mode, this
is always an object. undefined
and null
values are autoboxed to the global object (the window
object in the browser). In strict mode, however, it can be undefined
or null
, as there is no autoboxing of this
in strict mode.
function testThis() {
return this;
};
console.log(testThis()); // [object Window]
function testThisInStrictMode() {
'use strict'
return this;
};
console.log(testThis()); // undefined
In Objects
If we have an object with a method that uses this
and we call the method on the object, the object is automatically assigned to the method's this
.
const person = {
name: 'Abd',
age: 42,
sayHi: function() {
return `Hi, this is ${this.name}`;
},
};
console.log(person.sayHi()); // "Hi, this is Abd"
The same applies to instances of custom objects created using constructor functions, as well as classes.
// constructor function example
function Person() {
this.name = 'Abd';
this.age = 42;
this.sayHi = function() {
return `Hi, this is ${this.name}`;
};
};
const person = new Person();
console.log(person.sayHi()); // "Hi, this is Abd"
// class example
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
};
sayHi() {
return `Hi, this is ${this.name}`;
};
};
const person = new Person('Abd', 42);
console.log(person.sayHi()); // "Hi, this is Abd"
Function References
Probably the most highlight-able case about this
in JavaScript arises when we want to extract a reference of a method from an object and then call it from elsewhere.
For example, if we store the sayHi()
method of the person
object (from any of the examples above) in a variable and then invoke it later on, we will not have any object set for the method to act on. We are effectively detaching the object from the referenced function, so this
for this function at runtime will be either the global object or undefined
depending on whether in normal mode or strict mode.
`use strict`
const sayHiAbd = person.sayHi; // Note that person.sayHi is NOT being invoked here
console.log(sayHiAbd()); // Error: Cannot read property 'name' of undefined
In this scenario, sayHiAbd()
is like a standalone function defined as follows:
function sayHiAbd() {
return `Hi, this is ${this.name}`;
};
In such cases, we have to call the function using .call()
or .apply()
in order to set the this
object explicitly at the call site.
console.log(sayHiAbd.call({name: 'Abd', age: 42})); // "Hi, this is Abd"
Binding Permanently
If we want to permanently bind an object to the function, we have to create a new function with .bind()
, which attaches the object to the new function.
const alwaysSayHiAbd = sayHiAbd.bind({name: 'Abd', age: 42});
console.log(alwaysSayHiAbd()); // "Hi, this is Abd"
Arrow Syntax
The arrow syntax permanently binds the enclosing lexical context of the function definition to its execution context. So, the call site context never meddles with arrow functions.
In the object literal person
example above, if we modify our sayHi()
function to return an arrow function that returns the greeting string, the returned (arrow) function's this
gets bound to its enclosing lexical context, which is the person
object itself. Storing a reference to it and calling it always points its this
to person
.
const person = {
name: 'Abd',
age: 42,
sayHi: function() {
return () => `Hi, this is ${this.name}`;
},
};
const sayHiAbd = person.sayHi();
console.log(sayHiAbd()); // "Hi, this is Abd"
References
Top comments (0)