JavaScript this Keyword: Binding Rules and Contextual Behavior
TLDR: The this keyword is an implicit parameter that JavaScript functions receive at runtime, with its value determined entirely by how the function is invoked—following four precedence rules from constructor (new) down to default binding—while arrow functions capture this lexically from their surrounding scope.
The this keyword is one of JavaScript's most misunderstood features. According to the You-Dont-Know-JS repository by Kyle Simpson, this is not an author-time binding but a runtime mechanism that depends strictly on the call-site. Understanding these binding rules—as detailed in objects-classes/ch4.md—is essential for mastering object-oriented patterns and avoiding common context-related bugs.
What Is the this Keyword in JavaScript?
In JavaScript, this is an implicit parameter that the engine supplies to every function containing the identifier. Its value is not fixed at author-time; instead, it is determined at run-time by how the function is invoked. This dynamic binding allows functions to operate on different objects depending on the call-site context.
As implemented in the source code analysis, the JavaScript engine follows four distinct invocation patterns to set the this binding, evaluated in strict order of precedence.
The Four Rules of this Binding
The binding of this follows a clear precedence hierarchy. When multiple rules could apply, the engine uses the first matching pattern in the following order.
1. Constructor Invocation with new
The highest precedence rule applies when a function is invoked with the new keyword. This operation performs four steps:
- Creates a brand-new object
- Links the object's
[[Prototype]]to the function'sprototypeproperty - Calls the function with the new object bound as
this - If the function returns a non-object, the new object is returned instead
function Person(name) {
this.name = name;
}
const p = new Person('Ada');
// this → new object, p.name === 'Ada'
2. Explicit Binding with call and apply
The second precedence rule is explicit binding, where you directly specify the this context using Function.prototype.call() or Function.prototype.apply(). The first argument passed to either method becomes the this binding for that invocation.
function setX(x) {
this.x = x;
}
const other = { x: 0 };
setX.call(other, 20);
// this → other, other.x === 20
This pattern is commonly used for borrowing methods from other objects or fixing context for callbacks.
3. Implicit Binding with Method Calls
Implicit binding occurs when a function is accessed as a property of an object and invoked with dot notation (obj.method()). In this case, the object before the dot becomes the this binding.
const point = {
x: 0,
init(x) {
this.x = x;
}
};
point.init(5);
// this → point, point.x === 5
This is the most common pattern for object-oriented method calls in JavaScript.
4. Default Binding
If none of the above rules apply, default binding takes effect. The behavior depends on strict mode:
- Strict mode:
thisis set toundefined - Non-strict mode:
thisfalls back to the global object (globalThis)
function setX(x) {
this.x = x;
}
setX(10);
// Non-strict → globalThis.x === 10
// Strict mode → TypeError (cannot set property 'x' of undefined)
This rule explains why standalone function references lose their context.
Arrow Functions and Lexical this
Arrow functions (=>) behave differently from regular functions. According to objects-classes/ch5.md in the "An Arrow Points Somewhere" chapter, arrow functions do not have their own this binding.
Instead, they capture the this value of the lexical surrounding scope at the point where the arrow function is defined. Consequently, call, apply, and new have no effect on an arrow function's this.
function Counter() {
this.count = 0;
// Arrow keeps the surrounding `this` (the Counter instance)
this.inc = () => {
this.count++;
};
}
const c = new Counter();
const inc = c.inc; // detached from object
inc(); // still works: c.count === 1
As noted in objects-classes/ch6.md in the "Lexical This" section, this behavior eliminates the need for the classic var self = this pattern when working with callbacks.
// Arrow functions ignore explicit binding
const arrow = () => console.log(this);
arrow.call({ a: 1 }); // logs outer this, not { a: 1 }
Summary
- The
thiskeyword is a runtime binding, not an author-time reference, determined by how a function is invoked. - Four precedence rules govern
this: constructor (new), explicit (call/apply), implicit (method call), and default (global orundefinedin strict mode). - Arrow functions do not participate in these four rules; they inherit
thislexically from their enclosing scope. - Understanding these mechanisms, as detailed in objects-classes/ch4.md and related chapters, is essential for predictable object-oriented JavaScript.
Frequently Asked Questions
What determines the value of this in a JavaScript function?
The value of this is determined entirely by the call-site—how the function is invoked at runtime—not by where the function is declared. The JavaScript engine applies four precedence rules (constructor, explicit, implicit, default) to bind this to the appropriate object or value.
Why does this become undefined in strict mode?
In strict mode, default binding sets this to undefined rather than the global object. This prevents accidental pollution of the global namespace when standalone functions are called without context, throwing a TypeError if you attempt to access properties on undefined.
How do arrow functions handle this differently?
Arrow functions do not have their own this binding. Instead, they capture the this value from their lexical surrounding scope at definition time. This means call, apply, and new cannot change an arrow function's this, making them ideal for preserving context in callbacks.
What is the difference between implicit and explicit binding?
Implicit binding occurs when you call a function as a method using dot notation (obj.method()), automatically setting this to the object before the dot. Explicit binding uses call() or apply() to manually specify the this context as the first argument, overriding implicit binding but yielding to constructor (new) binding.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →