Movatterモバイル変換


[0]ホーム

URL:


Home
Dmitri Pavlutin
I help developers understand Frontend technologies
Post cover

Gentle Explanation of "this" in JavaScript

Updated October 24, 2020

1. The mystery of this

this keyword has been a mystery for me for a long time.

From a background like Java, PHP or otherstandard language,this is the instance of the current object in the class method.this cannot be used outside the method and such a simple approach does not create confusion.

In JavaScript the situation is different:this is the context of a function invocation (a.k.a. execution). The language has 4 function invocation types:

  • function invocation:alert('Hello World!')
  • method invocation:console.log('Hello World!')
  • constructor invocation:new RegExp('\\d')
  • indirect invocation:alert.call(undefined, 'Hello World!')

Each invocation type defines the context in its way, sothis behaves differently than the developer expects.

The mystery of this in JavaScript

Moreoverstrict mode also affects the execution context.

The key to understandingthis keyword is having a clear view of function invocation and how it impacts the context.

This article focuses on the invocation explanation, how the function invocation influencesthis and demonstrates the common pitfalls of identifying the value ofthis.

Before starting, let's familiarize with a couple of terms:

  • Invocation of a function is executing the code that makes the body of a function, or simplycalling the function. For exampleparseInt functioninvocation isparseInt('15').
  • Context of an invocation is the value ofthis within function body.
  • Scope of a function is the set of variables and functions accessible within a function body.

2. Function invocation

Function invocation is performed when an expression that evaluates to a function object is followed by an open parenthesis(, a comma separated list of arguments expressions and a close parenthesis). For exampleparseInt('18').

A simple example of function invocation:


function hello(name) {
return 'Hello ' + name + '!';
}
// Function invocation
const message = hello('World');

hello('World') is a function invocation:hello expression evaluates to a function object, followed by a pair of parenthesis with the'World' argument.

Function invocation expression cannot be aproperty accessorobj.myFunc(), which creates amethod invocation. For example[1,5].join(',') isnot a function invocation, but a method call. Please remember the distinction between them.

A more advanced example is theIIFE (immediately-invoked function expression):


// IIFE
const message = (function(name) {
return 'Hello ' + name + '!';
})('World');

IIFE is a function invocation too: the first pair of parenthesis(function(name) {...}) is an expression that evaluates to a function object, followed by the pair of parenthesis with'World' argument:('World').

2.1. this in a function invocation

this is theglobal object in a function invocation.

The global object is determined by the execution environment. In a browser, the global object iswindow object.

this in JavaScript function invocation

In a function invocation, the execution context is the global object.

Let's check the context in the following function:


function sum(a, b) {
console.log(this === window); // => true
this.myNumber = 20; // add 'myNumber' property to global object
return a + b;
}
// sum() is invoked as a function
// this in sum() is a global object (window)
sum(15, 16); // => 31
window.myNumber; // => 20

At the timesum(15, 16) is called, JavaScript automatically setsthis as the global object (window in a browser).

Whenthis is used outside of any function scope (the topmost scope: global execution context), it also equals to the global object:


console.log(this === window); // => true
this.myString = 'Hello World!';
console.log(window.myString); // => 'Hello World!'


<!-- In an html file -->
<script type="text/javascript">
console.log(this === window); // => true
</script>

2.2. this in a function invocation, strict mode

this isundefined in a function invocation in strict mode

The strict mode is available startingECMAScript 5.1, which is a restricted variant of JavaScript. It provides better security and stronger error checking.

To enable the strict mode place the directive'use strict' at the top of a function body.

Once enabled, the strict mode affects the execution context, makingthis to beundefined in a regular function invocation. The execution context isnot the global object anymore, contrary to above case2.1.

this in JavaScript function invocation, strict mode

An example of a function called in strict mode:


function multiply(a, b) {
'use strict'; // enable the strict mode
console.log(this === undefined); // => true
return a * b;
}
// multiply() function invocation with strict mode enabled
// this in multiply() is undefined
multiply(2, 5); // => 10

Whenmultiply(2, 5) is invoked as a function in strict mode,this isundefined.

The strict mode is active not only in the current scope but also in the inner scopes (for all functions declared inside):


function execute() {
'use strict';
function concat(str1, str2) {
// the strict mode is enabled too
console.log(this === undefined); // => true
return str1 + str2;
}
// concat() is invoked as a function in strict mode
// this in concat() is undefined
concat('Hello', ' World!'); // => "Hello World!"
}
execute();

'use strict' sits at the top ofexecute body, enabling the strict mode within its scope. Becauseconcat is declared within theexecute scope, it inherits the strict mode. And the invocationconcat('Hello', ' World!') makesthis to beundefined.

A single JavaScript file may contain both strict and non-strict modes. So it is possible to have different context behavior in a single script for the same invocation type:


function nonStrictSum(a, b) {
// non-strict mode
console.log(this === window); // => true
return a + b;
}
function strictSum(a, b) {
'use strict';
// strict mode is enabled
console.log(this === undefined); // => true
return a + b;
}
// nonStrictSum() is invoked as a function in non-strict mode
// this in nonStrictSum() is the window object
nonStrictSum(5, 6); // => 11
// strictSum() is invoked as a function in strict mode
// this in strictSum() is undefined
strictSum(8, 12); // => 20

2.3. Pitfall: this in an inner function

⚠️ A common trap with the function invocation is thinking thatthis is the same in an inner function as in the outer function.

👍 The context of the inner function (except arrow function) depends only on its own invocation type, but not on the outer function's context.

To makethis have a desired value, modify the inner function's context with indirect invocation (using.call() or.apply(), see5.) or create a bound function (using.bind(), see6.).

The following example is calculating a sum of two numbers:


const numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
// this is window or undefined in strict mode
console.log(this === numbers); // => false
return this.numberA + this.numberB;
}
return calculate();
}
};
numbers.sum(); // => NaN or throws TypeError in strict mode

⚠️numbers.sum() is a method invocation on an object (see3.) thusthis equalsnumbers.calculate() function is defined insidesum(), so you might expect to havethis asnumbers object when invokingcalculate() too.

calculate() is a function invocation (butnot method invocation), thus herethis is the global objectwindow (case2.1.) orundefined in strict mode (case2.2.). Even if the outer functionnumbers.sum() has the context asnumbers object, it doesn't have influence here.

The invocation result ofnumbers.sum() isNaN (or an error is thrownTypeError: Cannot read property 'numberA' of undefined in strict mode). Definitely not the expected result5 + 10 = 15. All becausecalculate() is not invoked correctly.

👍To solve the problem,calculate() function must execute with the same context as thenumbers.sum() method, to accessthis.numberA andthis.numberB properties.

One solution is to change manually the context ofcalculate() to the desired one by callingcalculate.call(this) (an indirect invocation of a function, see section5.):


const numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
console.log(this === numbers); // => true
return this.numberA + this.numberB;
}
// use .call() method to modify the context
return calculate.call(this);
}
};
numbers.sum(); // => 15

calculate.call(this) executescalculate() function as usual, but additionally modifies the context to a value specified as the first parameter.

Nowthis.numberA + this.numberB is same asnumbers.numberA + numbers.numberB. The function returns the expected result5 + 10 = 15.

Another solution, slightly better, is to use an arrow function:


const numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
const calculate = () => {
console.log(this === numbers); // => true
return this.numberA + this.numberB;
}
return calculate();
}
};
numbers.sum(); // => 15

The arrow function resolvesthis lexically, or, in other words, usesthis value ofnumbers.sum() method.

3. Method invocation

Amethod is a function stored in a property of an object. For example:


const myObject = {
// helloMethod is a method
helloMethod: function() {
return 'Hello World!';
}
};
const message = myObject.helloMethod();

helloMethod is a method ofmyObject. Use a property accessormyObject.helloMethod to access the method.

Method invocation is performed when an expression in a form ofproperty accessor that evaluates to a function object is followed by an open parenthesis(, a comma separated list of arguments expressions and a close parenthesis).

Recalling the previous example,myObject.helloMethod() is a method invocation ofhelloMethod on the objectmyObject.

More examples of method calls are:[1, 2].join(',') or/\s/.test('beautiful world').

Understanding the difference betweenfunction invocation (see section2.) andmethod invocation is important!

The method invocation requires aproperty accessor form to call the function (obj.myFunc() orobj['myFunc']()), while function invocation does not (myFunc()).


const words = ['Hello', 'World'];
words.join(', '); // method invocation
const obj = {
myMethod() {
return new Date().toString();
}
};
obj.myMethod(); // method invocation
const func = obj.myMethod;
func(); // function invocation
parseFloat('16.6'); // function invocation
isNaN(0); // function invocation

3.1. this in a method invocation

this is theobject that owns the method in a method invocation

When invoking a method on an object,this is the object that owns the method.

this in JavaScript method invocation

Let's create an object with a method that increments a number:


const calc = {
num: 0,
increment() {
console.log(this === calc); // => true
this.num += 1;
return this.num;
}
};
// method invocation. this is calc
calc.increment(); // => 1
calc.increment(); // => 2

Callingcalc.increment() makes the context ofincrement function to becalc object. So usingthis.num to increment the number property works well.

Let's follow another case. A JavaScript object inherits a method from itsprototype. When the inherited method is invoked on the object, the context of the invocation is still the object itself:


const myDog = Object.create({
sayName() {
console.log(this === myDog); // => true
return this.name;
}
});
myDog.name = 'Milo';
// method invocation. this is myDog
myDog.sayName(); // => 'Milo'

Object.create() creates a new objectmyDog and sets its prototype from the first argument.myDog object inheritssayName method.

WhenmyDog.sayName() is executed,myDog is the context of invocation.

In ECMAScript 2015class syntax, the method invocation context is also the instance itself:


class Planet {
constructor(name) {
this.name = name;
}
getName() {
console.log(this === earth); // => true
return this.name;
}
}
const earth = new Planet('Earth');
// method invocation. the context is earth
earth.getName(); // => 'Earth'

3.2. Pitfall: separating method from its object

⚠️ A method can be extracted from an object into a separated variableconst alone = myObj.myMethod. When the method is called alonealone(), detached from the original object, you might think thatthis is the objectmyObject on which the method was defined.

👍 Correctly if the method is called without an object, then a function invocation happens, wherethis is the global objectwindow orundefined in strict mode (see2.1 and2.2).

A bound functionconst alone = myObj.myMethod.bind(myObj) (using.bind(), see6.) fixes the context by bindingthis the object that owns the method.

The following example definesPet constructor and makes an instance of it:myCat. ThensetTimeout() after 1 second logsmyCat object information:


function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = function() {
console.log(this === myCat); // => false
console.log(`The ${this.type} has ${this.legs} legs`);
}
}
const myCat = new Pet('Cat', 4);
// logs "The undefined has undefined legs"
// or throws a TypeError in strict mode
setTimeout(myCat.logInfo, 1000);

⚠️ You might think thatsetTimeout(myCat.logInfo, 1000) will call themyCat.logInfo(), which should log the information aboutmyCat object.

Unfortunately the method is separated from its object when passed as a parameter:setTimeout(myCat.logInfo). The following cases are equivalent:


setTimeout(myCat.logInfo);
// is equivalent to:
const extractedLogInfo = myCat.logInfo;
setTimeout(extractedLogInfo);

When the separatedlogInfo is invoked as a function,this is global object orundefined in strict mode (butnotmyCat object). So the object information does not log correctly.

👍 A function bounds with an object using.bind() method (see6.). If the separated method is bound withmyCat object, the context problem is solved:


function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = function() {
console.log(this === myCat); // => true
console.log(`The ${this.type} has ${this.legs} legs`);
};
}
const myCat = new Pet('Cat', 4);
// Create a bound function
const boundLogInfo = myCat.logInfo.bind(myCat);
// logs "The Cat has 4 legs"
setTimeout(boundLogInfo, 1000);

myCat.logInfo.bind(myCat) returns a new function that executes exactly likelogInfo, but hasthis asmyCat, even in a function invocation.

An alternative solution is to definelogInfo() method as an arrow function, which bindsthis lexically:


function Pet(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = () => {
console.log(this === myCat); // => true
console.log(`The ${this.type} has ${this.legs} legs`);
};
}
const myCat = new Pet('Cat', 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo, 1000);

If you'd like to use classes and bindthis to the class instance in your method, use the arrow function as a class property:


class Pet {
constructor(type, legs) {
this.type = type;
this.legs = legs;
}
logInfo = () => {
console.log(this === myCat); // => true
console.log(`The ${this.type} has ${this.legs} legs`);
}
}
const myCat = new Pet('Cat', 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo, 1000);

4. Constructor invocation

Constructor invocation is performed whennew keyword is followed by an expression that evaluates to a function object, an open parenthesis(, a comma separated list of arguments expressions and a close parenthesis).

Examples of construction invocation:new Pet('cat', 4),new RegExp('\\d').

This example declares a functionCountry, then invokes it as a constructor:


function Country(name, traveled) {
this.name = name ? name : 'United Kingdom';
this.traveled = Boolean(traveled); // transform to a boolean
}
Country.prototype.travel = function() {
this.traveled = true;
};
// Constructor invocation
const france = new Country('France', false);
// Constructor invocation
const unitedKingdom = new Country;
france.travel(); // Travel to France

new Country('France', false) is a constructor invocation of theCountry function. This call creates a new object, whichname property is'France'.

If the constructor is called without arguments, then the parenthesis pair can be omitted:new Country.

Starting ECMAScript 2015, JavaScript allows to define constructors usingclass syntax:


class City {
constructor(name, traveled) {
this.name = name;
this.traveled = false;
}
travel() {
this.traveled = true;
}
}
// Constructor invocation
const paris = new City('Paris', false);
paris.travel();

new City('Paris') is a constructor invocation. The object's initialization is handled by a special method in the class:constructor, which hasthis as the newly created object.

The role of the constructor function is to initialize the instance. A constructor call creates a new empty object, which inherits properties from the constructor's prototype.

When a property accessormyObject.myFunction is preceded bynew keyword, JavaScript performs aconstructor invocation, butnot a method invocation.

For examplenew myObject.myFunction(): the function is first extracted using a property accessorextractedFunction = myObject.myFunction, then invoked as a constructor to create a new object:new extractedFunction().

4.1. this in a constructor invocation

this is thenewly created object in a constructor invocation

The context of a constructor invocation is the newly created object. The constructor initializes the object with data that comes from constructor arguments, sets up initial values for properties, attaches event handlers, etc.

this in JavaScript constructor invocation

Let's check the context in the following example:


function Foo () {
// this is fooInstance
this.property = 'Default Value';
}
// Constructor invocation
const fooInstance = new Foo();
fooInstance.property; // => 'Default Value'

new Foo() is making a constructor call where the context isfooInstance. InsideFoo the object is initialized:this.property is assigned with a default value.

The same scenario happens when usingclass syntax (available in ES2015), only the initialization happens in theconstructor method:


class Bar {
constructor() {
// this is barInstance
this.property = 'Default Value';
}
}
// Constructor invocation
const barInstance = new Bar();
barInstance.property; // => 'Default Value'

At the time whennew Bar() is executed, JavaScript creates an empty object and makes it the context of theconstructor() method. Now you can add properties to object usingthis keyword:this.property = 'Default Value'.

4.2. Pitfall: forgetting about new

Some JavaScript functions create instances not only when invoked as constructors, but also when invoked as functions. For exampleRegExp:


const reg1 = new RegExp('\\w+');
const reg2 = RegExp('\\w+');
reg1 instanceof RegExp; // => true
reg2 instanceof RegExp; // => true
reg1.source === reg2.source; // => true

When executingnew RegExp('\\w+') andRegExp('\\w+'), JavaScript creates equivalent regular expression objects.

⚠️ Using a function invocation to create objects is a potential problem (excludingfactory pattern), because some constructors may omit the logic to initialize the object whennew keyword is missing.

The following example illustrates the problem:


function Vehicle(type, wheelsCount) {
this.type = type;
this.wheelsCount = wheelsCount;
return this;
}
// Function invocation
const car = Vehicle('Car', 4);
car.type; // => 'Car'
car.wheelsCount // => 4
car === window // => true

Vehicle is a function that setstype andwheelsCount properties on the context object.When executingVehicle('Car', 4) an objectcar is returned, which has the correct properties:car.type is'Car' andcar.wheelsCount is4.

You might think it works well for creating and initializing new objects.

However,this iswindow object in a function invocation (see2.1.), thusVehicle('Car', 4) sets properties on thewindow object. This is a mistake. A new object is not created.

👍 Make sure to usenew operator in cases when a constructor call is expected:


function Vehicle(type, wheelsCount) {
if (!(this instanceof Vehicle)) {
throw Error('Error: Incorrect invocation');
}
this.type = type;
this.wheelsCount = wheelsCount;
return this;
}
// Constructor invocation
const car = new Vehicle('Car', 4);
car.type // => 'Car'
car.wheelsCount // => 4
car instanceof Vehicle // => true
// Function invocation. Throws an error.
const brokenCar = Vehicle('Broken Car', 3);

new Vehicle('Car', 4) works well: a new object is created and initialized becausenew keyword is present in the constructor invocation.

A verification is added in the constructor function:this instanceof Vehicle, to make sure that execution context is a correct object type — wheneverVehicle('Broken Car', 3) is executed withoutnew an exception is thrown:Error: Incorrect invocation.

5. Indirect invocation

Indirect invocation is performed when a function is called usingmyFun.call() ormyFun.apply() methods.

Functions in JavaScript are first-class objects, which means that a function is an object. The type of function object isFunction.

From thelist of methods that a function object has,.call() and.apply() are used to invoke the function with a configurable context.

myFunction.call(thisArg, arg1, arg2, ...) accepts the first argumentthisArg as the context of the invocation and a list of argumentsarg1, args2, ... that are passed as arguments to the called function.

myFunction.apply(thisArg, [arg1, arg2, ...]) accepts the first argumentthisArg as the context of the invocation and an array of arguments[arg1, args, ...] that are passed as arguments to the called function.

The following example demonstrates the indirect invocation:


function sum(number1, number2) {
return number1 + number2;
}
sum.call(undefined, 10, 2); // => 12
sum.apply(undefined, [10, 2]); // => 12

sum.call() andsum.apply() both invoke the function with10 and2 arguments.

5.1. this in an indirect invocation

this is thefirst argument of.call() or.apply() in an indirect invocation

this in indirect invocation is the value passed as first argument to.call() or.apply().

this in JavaScript indirect invocation

The following example shows the indirect invocation context:


const rabbit = { name: 'White Rabbit' };
function concatName(string) {
console.log(this === rabbit); // => true
return string + this.name;
}
// Indirect invocations
concatName.call(rabbit, 'Hello '); // => 'Hello White Rabbit'
concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'

The indirect invocation is useful when a function should be executed with a specific context. For example, to solve the context problems with function invocation, wherethis is alwayswindow orundefined in strict mode (see2.3.). It can be used to simulate a method call on an object (see the previous code sample).

Another practical example is creating hierarchies of classes in ES5 to call the parent constructor:


function Runner(name) {
console.log(this instanceof Rabbit); // => true
this.name = name;
}
function Rabbit(name, countLegs) {
console.log(this instanceof Rabbit); // => true
// Indirect invocation. Call parent constructor.
Runner.call(this, name);
this.countLegs = countLegs;
}
const myRabbit = new Rabbit('White Rabbit', 4);
myRabbit; // { name: 'White Rabbit', countLegs: 4 }

Runner.call(this, name) insideRabbit makes an indirect call of the parent function to initialize the object.

6. Bound function

A bound function is a function whose context and/or arguments are bound to specific values. You create a bound function using.bind() method. The original and bound functions share the same code and scope, but different contexts and arguments on execution.

myFunc.bind(thisArg[, arg1, arg2, ...) accepts the first argumentthisArg as the context and an optional list of argumentsarg1, arg2, ... to bound to..bind() returns a new function which context is bound tothisArg and arguments toarg1, arg2, ....

The following code creates a bound function and later invokes it:


function multiply(number) {
'use strict';
return this * number;
}
// create a bound function with context
const double = multiply.bind(2);
// invoke the bound function
double(3); // => 6
double(10); // => 20

multiply.bind(2) returns a new function objectdouble, which is bound with number2.multiply anddouble have the same code and scope.

Contrary to.apply() and.call() methods (see5.), which invoke the function right away, the.bind() method only returns a new function supposed to be invoked later with a pre-definedthis value.

6.1. this inside a bound function

this is thefirst argument ofmyFunc.bind(thisArg) when invoking a bound function

The role of.bind() is to create a new function, which invocation will have the context as the first argument passed to.bind(). It is a powerful technique that allows creating functions with a predefinedthis value.

this in JavaScript bound function invocation

Let's see how to configurethis of a bound function:


const numbers = {
array: [3, 5, 10],
getNumbers() {
return this.array;
}
};
// Create a bound function
const boundGetNumbers = numbers.getNumbers.bind(numbers);
boundGetNumbers(); // => [3, 5, 10]
// Extract method from object
const simpleGetNumbers = numbers.getNumbers;
simpleGetNumbers(); // => undefined or throws an error in strict mode

numbers.getNumbers.bind(numbers) returns a functionboundGetNumbers which context is bound tonumbers. ThenboundGetNumbers() is invoked withthis asnumbers and returns the correct array object.

The functionnumbers.getNumbers is extracted into a variablesimpleGetNumbers without binding. On later function invocationsimpleGetNumbers() hasthis aswindow orundefined in strict mode, but notnumbers object (see3.2. Pitfall). In this casesimpleGetNumbers() will not return correctly the array.

6.2. Tight context binding

.bind() makes apermanent context link and will always keep it. A bound function cannot change its linked context when using.call() or.apply() with a different context or even a rebound doesn't have any effect.

Only the constructor invocation of a bound function can change an already bound context, but this is not something you would normally do (constructor invocation must useregular, non-bound functions).

The following example creates a bound function, then tries to change its already pre-defined context:


function getThis() {
'use strict';
return this;
}
const one = getThis.bind(1);
one(); // => 1
one.call(2); // => 1
one.apply(2); // => 1
one.bind(2)(); // => 1
new one(); // => Object

Onlynew one() changes the context of the bound function. Other types of invocation always havethis equal to1.

7. Arrow function

Arrow function is designed to declare the function in a shorter form andlexically bind the context.

It can used the following way:


const hello = (name) => {
return 'Hello ' + name;
};
hello('World'); // => 'Hello World'
// Keep only even numbers
[1, 2, 5, 6].filter(item => item % 2 === 0); // => [2, 6]

Arrow functions have a light syntax, don't have the verbose keywordfunction. When the arrow function has only 1 statement, you could even omit thereturn keyword.

An arrow function isanonymous, but itsname can be inferred. It doesn't have a lexical function name (which would be useful for recursion, detaching event handlers).

Also it doesn't provide thearguments object, opposed to a regular function. The missingarguments is fixed using ES2015rest parameters:


const sumArguments = (...args) => {
console.log(typeof arguments); // => 'undefined'
return args.reduce((result, item) => result + item);
};
sumArguments.name // => ''
sumArguments(5, 5, 6); // => 16

7.1. this in arrow function

this is theenclosing context where the arrow function is defined

The arrow function doesn't create its own execution context but takesthis from the outer function where it is defined. In other words, the arrow function resolvesthis lexically.

this in JavaScript arrow function invocation

The following example shows the context transparency property:


class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
log() {
console.log(this === myPoint); // => true
setTimeout(() => {
console.log(this === myPoint); // => true
console.log(this.x + ':' + this.y); // => '95:165'
}, 1000);
}
}
const myPoint = new Point(95, 165);
myPoint.log();

setTimeout() calls the arrow function with the same context (myPoint object) as thelog() method. As seen, the arrow function "inherits" the context from the function where it is defined.

A regular function in this example creates its own context (window orundefined in strict mode). So to make the same code work correctly with a function expression it's necessary to manually bind the context:setTimeout(function() {...}.bind(this)). This is verbose, and using an arrow function is a cleaner and shorter solution.

If the arrow function is defined in the topmost scope (outside any function), the context is always the global object (window in a browser):


const getContext = () => {
console.log(this === window); // => true
return this;
};
console.log(getContext() === window); // => true

An arrow function is bound with the lexicalthisonce and forever.this cannot be modified even when using the context modification methods:


const numbers = [1, 2];
(function() {
const get = () => {
console.log(this === numbers); // => true
return this;
};
console.log(this === numbers); // => true
get(); // => [1, 2]
// Try to change arrow function context manually
get.call([0]); // => [1, 2]
get.apply([0]); // => [1, 2]
get.bind([0])(); // => [1, 2]
}).call(numbers);

No matter how the arrow functionget() is called, it always keeps the lexical contextnumbers. Indirect call with other contextget.call([0]) or. get.apply([0]), rebindingget.bind([0])() have no effect.

An arrow function cannot be used as a constructor. Invoking it as a constructornew get() throws an error:TypeError: get is not a constructor.

7.2. Pitfall: defining method with an arrow function

⚠️ You might want to use arrow functions to declare methods on an object. Fair enough: their declaration is quite short comparing to afunction expression:(param) => {...} instead offunction(param) {..}.

This example defines a methodformat() on a classPeriod using an arrow function:


function Period (hours, minutes) {
this.hours = hours;
this.minutes = minutes;
}
Period.prototype.format = () => {
console.log(this === window); // => true
return this.hours + ' hours and ' + this.minutes + ' minutes';
};
const walkPeriod = new Period(2, 30);
walkPeriod.format(); // => 'undefined hours and undefined minutes'

Sinceformat is an arrow function and is defined in the global context (topmost scope), it hasthis aswindow object.

Even ifformat is executed as a method on an objectwalkPeriod.format(),window is kept as the context of invocation. It happens because the arrow function has a static context that doesn't change on different invocation types.

The method returns'undefined hours and undefined minutes', which is not the expected result.

👍 The function expression solves the problem because a regular functiondoes change its context depending on invocation:


function Period (hours, minutes) {
this.hours = hours;
this.minutes = minutes;
}
Period.prototype.format = function() {
console.log(this === walkPeriod); // => true
return this.hours + ' hours and ' + this.minutes + ' minutes';
};
const walkPeriod = new Period(2, 30);
walkPeriod.format(); // => '2 hours and 30 minutes'

walkPeriod.format() is a method invocation on an object (see3.1.) with the contextwalkPeriod object.this.hours evaluates to2 andthis.minutes to30, so the method returns the correct result:'2 hours and 30 minutes'.

8. Conclusion

Because the function invocation has the biggest impact onthis, from now ondo not ask yourself:

Where isthis taken from?

butdo ask yourself:

How is the*`function invoked*?

For an arrow function ask yourself:

What isthis inside the outer function where the arrow function isdefined?

This mindset is correct when dealing withthis and will save you from the headache.

If you have an interesting example of context pitfall or just experience difficulties with a case, write a comment below, and let's discuss!

Like the post? Please share!

Dmitri Pavlutin

About Dmitri Pavlutin

Software developer and sometimes writer. My daily routine consists of (but not limited to) drinking coffee, coding, writing, overcoming boredom 😉, developinga gift boxes Shopify app, andblogging about Shopify. Living in the sunny Barcelona. 🇪🇸
Email addressTwitter profileFacebook pageLinkedIn profile
Dmitri Pavlutin

About Dmitri Pavlutin

Software developer and sometimes writer. My daily routine consists of (but not limited to) drinking coffee, coding, writing, overcoming boredom 😉, developinga gift boxes Shopify app, andblogging about Shopify. Living in the sunny Barcelona. 🇪🇸
Email addressTwitter profileFacebook pageLinkedIn profile

[8]ページ先頭

©2009-2025 Movatter.jp