this in JavaScript according to official specification
Published April 25, 2025Understanding this
in JavaScript
The behavior of this
in JavaScript can be confusing, but it’s critical to understand how it works in different contexts. This article breaks down how this
behaves in various environments, such as the global scope, normal functions, arrow functions, and when invoked through different mechanisms like call
, apply
, bind
, or the new
operator.
Before We Dive In
To understand how this
works, we need to grasp two key concepts about JavaScript execution:
- JavaScript code execution always involves running a function.
- Execution happens in two stages:
- Creating the function’s surrounding environment (context).
- Executing the function’s code.
Information: These stages are critical because the value of
this
is determined at the moment a function is called, based on how it’s invoked.
Important Definitions
Here are the core definitions to understand this
in JavaScript:
this
is not the context itself but a special identifier (variable) defined locally for all "normal" functions.- By default,
this
isundefined
in strict mode or the global object in non-strict (sloppy) mode. - The value of
this
can only be set at the moment of a function’s invocation, depending on the form and method of the call. - External APIs can bind
this
to any value, depending on the API author’s needs.
Additional Definitions
What is a "Normal Function"?
A normal function is any function that is not an arrow function. This distinction is important because arrow functions handle this
differently.
What is Dot Notation?
Dot notation refers to the syntax where two identifiers are separated by a dot, e.g., theObj.theProperty
. For function calls, it looks like theObj.doThing()
or theObj["doThing"]()
, which are equivalent.
Tip: Understanding dot notation is key when analyzing
this
in method calls, as it directly affects whatthis
is refers to.
this
in the Global Environment
How do we determine the value of this
in the global environment?
-
Identify whether
this
is accessed in a script or a module. -
The value of
this
depends on the host system (e.g., browser or Node.js). -
In Node.js,
this
in the global scope is an empty object. -
In browsers,
this
is typically the global object (window
). -
According to the JavaScript specification,
this
in the global environment can be anything, depending on the host.
this
in a Module Script
'use strict'; // Global module scope console.log(this, 'this in module'); // Result: undefined
Let’s answer the following questions:
- Are we inside a function? No.
- Are we in the global environment? Yes.
- Is it a module? Yes.
- In this case,
this
isundefined
.
this
in a Default Script
'use strict'; // Global default scope console.log(this, 'this in script'); // Result: global object (window in browsers)
Let’s answer the following questions:
- Are we inside a function? No.
- Are we in the global environment? Yes.
- Is it a module? No.
- Is it a default script? Yes.
- In this case,
this
equals the global object (e.g.,window
in browsers).
Warning: The value of
this
in the global scope depends on the host environment. If the host modifiesthis
, it will differ from the default global object.
this
in Normal Functions
To determine what this
is inside a normal function, we must examine how the function is called. Let’s explore different invocation methods.
Direct Function Call
'use strict'; function doThing() { console.log(this, 'this in function'); } doThing(); // Result: undefined
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? No.
- Is it called via
call
,apply
, or transformed bybind
? No. - Is it called via the
new
operator? No. - Is it called via dot notation? No.
- Is it called via an external API? No.
- In this case,
this
isundefined
(in strict mode).
Using call
, apply
, or bind
When a function is invoked with call
, apply
, or bind
, this
is set to the value passed to these methods.
'use strict'; function doThing(arg) { console.log(this, `this in function with ${arg}`); console.log('I am new this value' === this); // true } const newThisValue = 'I am new this value'; doThing.call(newThisValue, 'call'); doThing.apply(newThisValue, ['apply']); doThing.bind(newThisValue, 'bind')();
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? No.
- Is it called via
call
,apply
, orbind
? Yes. - In this case,
this
equals the value passed tocall
,apply
, orbind
(here,newThisValue
).
Using the new
Operator
When a function is invoked with the new
operator, this
is bound to a new empty object (unless explicitly set inside the function).
'use strict'; function doThing() { console.log(this, 'this in function called by new operator'); console.log(this instanceof Object && Object.keys(this).length === 0); // true } new doThing();
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? No.
- Is it called via
call
,apply
, orbind
? No. - Is it called via the
new
operator? Yes. - In this case,
this
is a new empty object.
Warning: Not all functions can be invoked with
new
. Only functions with an internal[[Construct]]
method can be used as constructors. Arrow functions, bound functions, and object methods lack this method and will throw aTypeError
.
'use strict'; function doThing() { console.log('bind'); } const arrowFunction = () => console.log('arrow'); const obj = { shortMethod() { console.log('shortMethod'); } }; new doThing.bind(undefined); // TypeError: is not a constructor new arrowFunction; // TypeError: is not a constructor new obj.shortMethod; // TypeError: is not a constructor
Using Dot Notation
When a function is called via dot notation, this
is bound to the identifier before the dot.
'use strict'; function doThing() { console.log('dot notation', this); console.log('doThing' in this); // true } const obj = {}; obj.doThing = doThing; obj.doThing(); // or obj["doThing"]()
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? No.
- Is it called via
call
,apply
, orbind
? No. - Is it called via the
new
operator? No. - Is it called via dot notation? Yes.
- In this case,
this
equals the identifier before the dot (here,obj
).
Using External APIs
When a function is passed to an external API (e.g., setTimeout
), the value of this
depends on the API’s implementation.
'use strict'; function doThing() { console.log('timeout', this); } setTimeout(doThing, 1); // In browsers: window; In Node.js: Timeout class
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? No.
- Is it called via
call
,apply
, orbind
? No. - Is it called via the
new
operator? No. - Is it called via dot notation? No.
- Is it called via an external API? Yes.
- In this case,
this
depends on the API’s documentation (e.g.,window
in browsers,Timeout
in Node.js).
Tip: Always check the documentation of the external API to understand how it binds
this
. Without explicitcall
,apply
, orbind
, the value can vary.
this
in Arrow Functions
Arrow functions do not have their own this
. Instead, they inherit this
from their parent lexical environment.
'use strict'; function doThing() { (() => console.log('arrow inside', this))(); } doThing(); // Result: undefined
Let’s answer the following questions:
- Are we inside a function? Yes.
- Is it an arrow function? Yes.
- Is the parent environment a normal function? Yes.
- In this case,
this
is inherited from the parent function’sthis
(here,undefined
becausedoThing
was called directly).
Information: Arrow functions are powerful for preserving the
this
value from their surrounding scope, but this behavior can be confusing if you expect them to behave like normal functions.
Final Thoughts
Understanding this
in JavaScript requires careful attention to how and where a function is called. Whether in the global environment, normal functions, or arrow functions, the value of this
is determined by specific rules tied to the invocation method. By mastering these rules, you can avoid common pitfalls and write more predictable code.
Special thanks to the content creator “Demi Murych” for shedding light on JavaScript’s intricacies through their YouTube channel As For JS. Their work inspired this article and helps developers navigate the complexities of the language.
Dmytro Notes