author face photo

this in JavaScript according to official specification

Published April 25, 2025

Understanding 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 is undefined 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 what this 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 is undefined.

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 modifies this, 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 by bind? 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 is undefined (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, or bind? Yes.
  • In this case, this equals the value passed to call, apply, or bind (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, or bind? 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 a TypeError.

'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, or bind? 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, or bind? 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 explicit call, apply, or bind, 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’s this (here, undefined because doThing 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