docs/concepts/this-call-apply-bind.mdx
Why does this sometimes point to the wrong object? Why does your method work perfectly when called directly, but break when passed as a callback? And how do call, apply, and bind let you take control?
const user = {
name: "Alice",
greet() {
return `Hi, I'm ${this.name}`;
}
};
user.greet(); // "Hi, I'm Alice" - works!
const greet = user.greet;
greet(); // "Hi, I'm undefined" - broken!
The this keyword is one of JavaScript's most confusing features, but it follows specific rules. According to the Stack Overflow Developer Survey, this binding is consistently cited as one of the trickiest parts of JavaScript for developers to master. Once you understand them, you'll never be confused again.
Think about the word "I" in everyday conversation. It's a simple word, but its meaning changes completely depending on who is speaking:
┌─────────────────────────────────────────────────────────────────┐
│ THE PRONOUN "I" │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Alice says: "I am a developer" │
│ ↓ │
│ "I" = Alice │
│ │
│ Bob says: "I am a designer" │
│ ↓ │
│ "I" = Bob │
│ │
│ The SAME word "I" refers to DIFFERENT people │
│ depending on WHO is speaking! │
│ │
└─────────────────────────────────────────────────────────────────┘
This is exactly how this works in JavaScript! The keyword this is like the pronoun "I". It refers to different objects depending on who is "speaking" (which object is calling the function).
const alice = {
name: "Alice",
introduce() {
return "I am " + this.name; // "I" = this = alice
}
};
const bob = {
name: "Bob",
introduce() {
return "I am " + this.name; // "I" = this = bob
}
};
alice.introduce(); // "I am Alice"
bob.introduce(); // "I am Bob"
But here's where JavaScript gets interesting. What if Alice could make Bob say her words? Like a ventriloquist making a puppet speak?
// Alice borrows Bob's voice to introduce herself
bob.introduce.call(alice); // "I am Alice" (Bob's function, Alice's this)
That's what call, apply, and bind do. They let you control who "I" refers to, regardless of which function is speaking.
this in JavaScript?The this keyword is a special identifier that JavaScript automatically creates in every function execution context. It refers to the object that is currently executing the code, typically the object that "owns" the method being called. Unlike most languages where this is fixed at definition time, JavaScript determines this dynamically at call time, based on how a function is invoked.
Here's what makes JavaScript different from many other languages:
thisis determined when the function is CALLED, not when it's defined.
This is called dynamic binding, and it's both powerful and confusing. The same function can have different this values depending on how you call it:
function showThis() {
return this;
}
const obj = { showThis };
// Same function, different this values:
showThis(); // undefined (strict mode) or globalThis
obj.showThis(); // obj (the object before the dot)
showThis.call({}); // {} (explicitly specified)
In non-strict mode, plain function calls return globalThis (the global object: window in browsers, global in Node.js).
This design allows for incredible flexibility:
// One function, many objects
function greet() {
return `Hello, I'm ${this.name}!`;
}
const alice = { name: "Alice", greet };
const bob = { name: "Bob", greet };
const charlie = { name: "Charlie", greet };
alice.greet(); // "Hello, I'm Alice!"
bob.greet(); // "Hello, I'm Bob!"
charlie.greet(); // "Hello, I'm Charlie!"
The trade-off? You need to understand the rules that determine this. Let's dive in.
When JavaScript needs to figure out what this refers to, it follows these rules in order of priority. Higher priority rules override lower ones.
BINDING RULES (Highest to Lowest Priority)
┌─────────────────────────────────────────┐
│ 1. new Binding (Highest) │
├─────────────────────────────────────────┤
│ 2. Explicit Binding (call/apply/ │
│ bind) │
├─────────────────────────────────────────┤
│ 3. Implicit Binding (method call) │
├─────────────────────────────────────────┤
│ 4. Default Binding (plain call) │
├─────────────────────────────────────────┤
│ 5. Arrow Functions (lexical) │
│ (Special case - no own this) │
└─────────────────────────────────────────┘
new Binding (Highest Priority)When a function is called with the new keyword, this is set to a brand new object that's automatically created.
class Person {
constructor(name) {
// 'this' is the new object being created
this.name = name;
this.greet = function() {
return `Hi, I'm ${this.name}`;
};
}
}
const alice = new Person("Alice");
console.log(alice.name); // "Alice"
console.log(alice.greet()); // "Hi, I'm Alice"
new Does Under the HoodWhen you call new Person("Alice"), JavaScript performs these 4 steps:
```javascript
// Conceptually:
newObject.__proto__ = Person.prototype;
```
```javascript
// Conceptually:
Person.call(newObject, "Alice");
```
```javascript
// Conceptually:
return newObject;
```
Here's a simplified implementation of what new does:
// What 'new' does behind the scenes
function simulateNew(Constructor, ...args) {
// Step 1: Create empty object
const newObject = {};
// Step 2: Link prototype if it's an object
// (If prototype isn't an object, newObject keeps Object.prototype)
if (Constructor.prototype !== null && typeof Constructor.prototype === 'object') {
Object.setPrototypeOf(newObject, Constructor.prototype);
}
// Step 3: Bind this and execute
const result = Constructor.apply(newObject, args);
// Step 4: Return object (unless constructor returns a non-primitive)
return result !== null && typeof result === 'object' ? result : newObject;
}
// These are equivalent:
const alice1 = new Person("Alice");
const alice2 = simulateNew(Person, "Alice");
With ES6 classes, the syntax is cleaner but the behavior is identical:
class Rectangle {
constructor(width, height) {
this.width = width; // 'this' = new Rectangle instance
this.height = height;
}
getArea() {
return this.width * this.height; // 'this' = the instance
}
}
const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 50
call, apply, bind)You can explicitly specify what this should be using call(), apply(), or bind(). This overrides implicit and default binding.
function introduce() {
return `I'm ${this.name}, a ${this.role}`;
}
const alice = { name: "Alice", role: "developer" };
const bob = { name: "Bob", role: "designer" };
// Explicitly set 'this' to alice
introduce.call(alice); // "I'm Alice, a developer"
// Explicitly set 'this' to bob
introduce.call(bob); // "I'm Bob, a designer"
We'll cover call, apply, and bind in detail in the next section. For now, just know that explicit binding has higher priority than implicit binding:
const alice = {
name: "Alice",
greet() {
return `Hi, I'm ${this.name}`;
}
};
const bob = { name: "Bob" };
// Even though we're calling alice.greet(), we can override 'this'
alice.greet.call(bob); // "Hi, I'm Bob" (explicit wins!)
When a function is called as a method of an object (using dot notation), this is set to the object before the dot.
const user = {
name: "Alice",
greet() {
return `Hello, I'm ${this.name}`;
}
};
// The object before the dot becomes 'this'
user.greet(); // "Hello, I'm Alice" (this = user)
A simple way to remember: look left of the dot when the function is called.
const company = {
name: "TechCorp",
department: {
name: "Engineering",
getName() {
return this.name;
}
}
};
// What's left of the dot at call time?
company.department.getName(); // "Engineering" (this = department)
This trips up many developers. When you extract a method from an object, it loses its implicit binding:
const user = {
name: "Alice",
greet() {
return `Hello, I'm ${this.name}`;
}
};
// This works
user.greet(); // "Hello, I'm Alice"
// But extracting the method loses 'this'!
const greetFn = user.greet;
greetFn(); // "Hello, I'm undefined" (strict mode: this = undefined)
Why? Because greetFn() is a plain function call. There's no dot, so implicit binding doesn't apply. We fall through to default binding.
IMPLICIT BINDING LOST
┌─────────────────────────────────────────────────────────────┐
│ │
│ user.greet() │
│ ↑ │
│ └── Object before dot → this = user ✓ │
│ │
│ const greetFn = user.greet; │
│ greetFn() │
│ ↑ │
│ └── No dot! → Default binding → this = undefined ✗ │
│ │
└─────────────────────────────────────────────────────────────┘
This happens constantly with:
setTimeout(user.greet, 1000)button.addEventListener('click', user.greet)[1,2,3].forEach(user.process)We'll cover solutions in the "Gotchas" section.
When a function is called without any of the above conditions, default binding applies.
In strict mode (which you should always use): this is undefined.
In non-strict mode: this is the global object (window in browsers, global in Node.js).
"use strict";
function showThis() {
return this;
}
showThis(); // undefined (strict mode)
// Without strict mode (not recommended)
function showThis() {
return this;
}
showThis(); // window (in browser) or global (in Node.js)
ES6 modules and classes are automatically in strict mode. </Warning>
Default binding kicks in when:
myFunction()(function() { ... })()setTimeout(function() { ... }, 100)"use strict";
// All of these use default binding (this = undefined)
function regularFunction() {
return this;
}
regularFunction(); // undefined
(function() {
return this; // undefined
})();
setTimeout(function() {
console.log(this); // undefined
}, 100);
this)Arrow functions are special. They don't have their own this binding at all. Instead, they inherit this from their enclosing scope at the time they're defined.
const user = {
name: "Alice",
// Regular function: 'this' is determined by how it's called
regularGreet: function() {
return `Hi, I'm ${this.name}`;
},
// Arrow function: 'this' is inherited from where it's defined
arrowGreet: () => {
return `Hi, I'm ${this.name}`;
}
};
user.regularGreet(); // "Hi, I'm Alice" (this = user)
user.arrowGreet(); // "Hi, I'm undefined" (this = enclosing scope, not user!)
Wait, why is arrowGreet showing undefined? Because the arrow function was defined in the object literal, and the enclosing scope at that point is the module/global scope, not the user object.
Arrow functions are perfect for callbacks where you want to preserve the outer this:
class Counter {
constructor() {
this.count = 0;
}
// Problem: regular function loses 'this' in callback
startBroken() {
setInterval(function() {
this.count++; // ERROR: 'this' is undefined!
console.log(this.count);
}, 1000);
}
// Solution: arrow function preserves 'this'
startFixed() {
setInterval(() => {
this.count++; // Works! 'this' is the Counter instance
console.log(this.count);
}, 1000);
}
}
You cannot change an arrow function's this using call, apply, or bind:
const arrowFn = () => this;
const obj = { name: "Object" };
// These all return the same thing - the lexical 'this'
arrowFn(); // lexical this
arrowFn.call(obj); // lexical this (call is ignored!)
arrowFn.apply(obj); // lexical this (apply is ignored!)
arrowFn.bind(obj)(); // lexical this (bind is ignored!)
A common modern pattern is using arrow functions as class methods:
class Button {
constructor(label) {
this.label = label;
}
// Arrow function as class field - 'this' is always the instance
handleClick = () => {
console.log(`Button "${this.label}" clicked`);
}
}
const btn = new Button("Submit");
// Works even when extracted!
const handler = btn.handleClick;
handler(); // "Button "Submit" clicked" ✓
// Works in event listeners!
document.querySelector('button').addEventListener('click', btn.handleClick);
When you need to figure out what this is, follow this flowchart:
┌─────────────────────────┐
│ Is it an arrow │
│ function? │
└───────────┬─────────────┘
│ │
YES ◄──┘ └──► NO
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ this = enclosing│ │ Was it called with │
│ scope's this │ │ 'new'? │
│ (DONE) │ └──────────┬──────────┘
└─────────────────┘ │ │
YES ◄─┘ └──► NO
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ this = new │ │ Was call/apply/bind │
│ object │ │ used? │
│ (DONE) │ └──────────┬──────────┘
└─────────────────┘ │ │
YES ◄──┘ └──► NO
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ this = specified│ │ Was it called as │
│ object │ │ obj.method()? │
│ (DONE) │ └──────────┬──────────┘
└─────────────────┘ │ │
YES ◄──┘ └──► NO
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ this = obj │ │ Default binding:│
│ (left of dot) │ │ this = undefined│
│ (DONE) │ │ (strict mode) │
└─────────────────┘ └─────────────────┘
call(), apply(), and bind() Work?These three methods give you explicit control over this. They're built into every function in JavaScript.
| Method | Invokes Function? | Arguments | Returns |
|---|---|---|---|
call() | Yes, immediately | Individual: call(this, a, b, c) | Function result |
apply() | Yes, immediately | Array: apply(this, [a, b, c]) | Function result |
bind() | No | Individual: bind(this, a, b) | New function |
Memory trick:
call() — Call with ThisThe call() method calls a function with a specified this value and arguments provided individually.
Syntax:
func.call(thisArg, arg1, arg2, ...)
Basic example:
function greet(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const alice = { name: "Alice" };
const bob = { name: "Bob" };
greet.call(alice, "Hello", "!"); // "Hello, I'm Alice!"
greet.call(bob, "Hi", "..."); // "Hi, I'm Bob..."
call() is perfect for borrowing methods from one object to use on another:
const arrayLike = {
0: "a",
1: "b",
2: "c",
length: 3
};
// arrayLike doesn't have array methods, but we can borrow them!
const result = Array.prototype.slice.call(arrayLike);
console.log(result); // ["a", "b", "c"]
const joined = Array.prototype.join.call(arrayLike, "-");
console.log(joined); // "a-b-c"
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
speak() {
// Call parent method with 'this' context
const parentSays = Animal.prototype.speak.call(this);
return `${parentSays}. ${this.name} barks!`;
}
}
const dog = new Dog("Rex");
dog.speak(); // "Rex makes a sound. Rex barks!"
apply() — Apply with ArrayThe apply() method is almost identical to call(), but arguments are passed as an array (or array-like object).
Syntax:
func.apply(thisArg, [arg1, arg2, ...])
Basic example:
function greet(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const alice = { name: "Alice" };
// Same result as call(), but args in an array
greet.apply(alice, ["Hello", "!"]); // "Hello, I'm Alice!"
Before ES6, apply() was the way to use Math.max() with an array:
const numbers = [5, 2, 9, 1, 7];
// Old way with apply
const max = Math.max.apply(null, numbers); // 9
const min = Math.min.apply(null, numbers); // 1
const numbers = [5, 2, 9, 1, 7];
const max = Math.max(...numbers); // 9
const min = Math.min(...numbers); // 1
The spread syntax is cleaner and more readable. Use apply() mainly when you need to set this AND spread arguments.
</Tip>
apply() shines when your arguments are already in array form:
function introduce(greeting, role, company) {
return `${greeting}! I'm ${this.name}, ${role} at ${company}.`;
}
const alice = { name: "Alice" };
const args = ["Hello", "engineer", "TechCorp"];
// When args are already an array, apply is natural
introduce.apply(alice, args); // "Hello! I'm Alice, engineer at TechCorp."
// With call, you'd need to spread
introduce.call(alice, ...args); // Same result
bind() — Bind for LaterThe bind() method is different from call() and apply(). It doesn't call the function immediately. Instead, it returns a new function with this permanently bound.
Syntax:
const boundFunc = func.bind(thisArg, arg1, arg2, ...)
Basic example:
function greet() {
return `Hello, I'm ${this.name}`;
}
const alice = { name: "Alice" };
// bind() returns a NEW function
const greetAlice = greet.bind(alice);
// Call it whenever you want
greetAlice(); // "Hello, I'm Alice"
greetAlice(); // "Hello, I'm Alice" (still works!)
Once bound, the this value cannot be changed, not even with call() or apply():
function showThis() {
return this.name;
}
const alice = { name: "Alice" };
const bob = { name: "Bob" };
const boundToAlice = showThis.bind(alice);
boundToAlice(); // "Alice"
boundToAlice.call(bob); // "Alice" (call ignored!)
boundToAlice.apply(bob); // "Alice" (apply ignored!)
boundToAlice.bind(bob)(); // "Alice" (bind ignored!)
This is a common use of bind():
class Toggle {
constructor() {
this.isOn = false;
// Without bind, 'this' would be the button element
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.isOn = !this.isOn;
console.log(`Toggle is ${this.isOn ? 'ON' : 'OFF'}`);
}
attachTo(button) {
button.addEventListener('click', this.handleClick);
}
}
class Countdown {
constructor(start) {
this.count = start;
}
start() {
// Without bind, 'this' would be undefined in the callback
setInterval(this.tick.bind(this), 1000);
}
tick() {
console.log(this.count--);
}
}
const countdown = new Countdown(10);
countdown.start(); // 10, 9, 8, 7...
bind() can also pre-fill arguments, creating a specialized version of a function:
function multiply(a, b) {
return a * b;
}
// Create specialized functions
const double = multiply.bind(null, 2); // 'a' is always 2
const triple = multiply.bind(null, 3); // 'a' is always 3
double(5); // 10 (2 * 5)
triple(5); // 15 (3 * 5)
double(7); // 14 (2 * 7)
This technique is called partial application. You're partially applying arguments to create a more specific function.
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
// Partial application: pre-fill the greeting
const sayHello = greet.bind(null, "Hello");
const sayGoodbye = greet.bind(null, "Goodbye");
sayHello("Alice"); // "Hello, Alice!"
sayHello("Bob"); // "Hello, Bob!"
sayGoodbye("Alice"); // "Goodbye, Alice!"
Use array methods on array-like objects:
// Arguments object (old-school, but still seen in legacy code)
function sum() {
// 'arguments' is array-like but not an array (see MDN: Arguments object)
return Array.prototype.reduce.call(
arguments,
(total, n) => total + n,
0
);
}
sum(1, 2, 3, 4); // 10
// NodeList from DOM (browser-only example)
const divs = document.querySelectorAll('div'); // NodeList, not Array
const texts = Array.prototype.map.call(divs, div => div.textContent);
// Modern alternative: Array.from()
const textsModern = Array.from(divs).map(div => div.textContent);
// Or spread
const textsSpread = [...divs].map(div => div.textContent);
The three main approaches to ensure this is correct in class methods:
class Player {
constructor(name) {
this.name = name;
this.score = 0;
// Approach 1: Bind in constructor
this.incrementBound = this.incrementBound.bind(this);
}
// Regular method - needs binding when used as callback
incrementBound() {
this.score++;
return this.score;
}
// Approach 2: Arrow function class field
incrementArrow = () => {
this.score++;
return this.score;
}
// Approach 3: Bind at call site (inline)
regularIncrement() {
this.score++;
return this.score;
}
}
const player = new Player("Alice");
// All these work correctly:
setTimeout(player.incrementBound, 100); // Approach 1
setTimeout(player.incrementArrow, 100); // Approach 2
setTimeout(player.regularIncrement.bind(player), 100); // Approach 3
setTimeout(() => player.regularIncrement(), 100); // Approach 3 alt
// Generic logging function
function log(level, timestamp, message) {
console.log(`[${level}] ${timestamp}: ${message}`);
}
// Create specialized loggers
const logError = log.bind(null, "ERROR");
const logWarning = log.bind(null, "WARNING");
const logInfo = log.bind(null, "INFO");
const now = new Date().toISOString();
logError(now, "Database connection failed");
// [ERROR] 2024-01-15T10:30:00.000Z: Database connection failed
logInfo(now, "Server started");
// [INFO] 2024-01-15T10:30:00.000Z: Server started
this Goes Wrong start() {
setInterval(function() {
this.seconds++; // ERROR: this is undefined!
console.log(this.seconds);
}, 1000);
}
}
```
**Why it happens:** The callback function uses default binding, so `this` is `undefined` in strict mode.
**Solutions:**
```javascript
// Solution 1: Arrow function
start() {
setInterval(() => {
this.seconds++; // ✓ Arrow inherits 'this'
}, 1000);
}
// Solution 2: bind()
start() {
setInterval(function() {
this.seconds++; // ✓ Bound to Timer instance
}.bind(this), 1000);
}
// Solution 3: Store reference (old-school)
start() {
const self = this;
setInterval(function() {
self.seconds++; // ✓ Using closure
}, 1000);
}
```
const greet = user.greet;
greet(); // "Hi, I'm undefined"
```
**Why it happens:** Assigning the method to a variable loses the implicit binding.
**Solutions:**
```javascript
// Solution 1: Keep as method call
user.greet(); // ✓ "Hi, I'm Alice"
// Solution 2: Bind when extracting
const greet = user.greet.bind(user);
greet(); // ✓ "Hi, I'm Alice"
// Solution 3: Wrapper function
const greet = () => user.greet();
greet(); // ✓ "Hi, I'm Alice"
```
add(numbers) {
numbers.forEach(function(n) {
this.value += n; // ERROR: this is undefined!
});
return this.value;
}
};
```
**Why it happens:** The inner function has its own `this` (default binding), it doesn't inherit from `add()`.
**Solutions:**
```javascript
// Solution 1: Arrow function (recommended)
add(numbers) {
numbers.forEach((n) => {
this.value += n; // ✓ Arrow inherits 'this'
});
return this.value;
}
// Solution 2: Use thisArg parameter
add(numbers) {
numbers.forEach(function(n) {
this.value += n; // ✓ 'this' passed as second arg
}, this);
return this.value;
}
// Solution 3: bind()
add(numbers) {
numbers.forEach(function(n) {
this.value += n; // ✓ Bound to calculator
}.bind(this));
return this.value;
}
```
user.greet(); // "Hi, I'm undefined"
```
**Why it happens:** Arrow functions don't have their own `this`. The `this` here is from the surrounding scope (module/global), not `user`.
**Solution:** Use regular functions for object methods:
```javascript
const user = {
name: "Alice",
greet() { // Shorthand method syntax
return `Hi, I'm ${this.name}`; // ✓ this = user
}
};
user.greet(); // "Hi, I'm Alice"
```
Arrow functions were introduced in ES6 partly to solve this confusion. They work fundamentally differently.
thisthis: Arrow functions don't create their own this bindingthis from the enclosing scopecall, apply, or bindconst obj = {
name: "Object",
regularMethod: function() {
console.log("Regular:", this.name); // "Object"
// Nested regular function - loses 'this'
function inner() {
console.log("Inner regular:", this); // undefined
}
inner();
// Nested arrow function - keeps 'this'
const innerArrow = () => {
console.log("Inner arrow:", this.name); // "Object"
};
innerArrow();
}
};
| Use Case | Arrow Function | Regular Function |
|---|---|---|
| Object methods | ❌ No | ✅ Yes |
| Class methods (in prototype) | ❌ No | ✅ Yes |
Callbacks needing outer this | ✅ Yes | ❌ No (needs bind) |
| Event handlers in classes | ✅ Yes (as class fields) | ⚠️ Needs binding |
Functions needing own this | ❌ No | ✅ Yes |
| Constructor functions | ❌ No (can't use new) | ✅ Yes |
Methods using arguments | ❌ No (no arguments) | ✅ Yes |
This is the most common pattern in modern JavaScript:
class SearchBox {
constructor(element) {
this.element = element;
this.query = "";
// Attach event listener - arrow function ensures correct 'this'
this.element.addEventListener('input', this.handleInput);
}
// Arrow function as class field
handleInput = (event) => {
this.query = event.target.value; // 'this' is always SearchBox instance
this.performSearch();
}
performSearch = () => {
console.log(`Searching for: ${this.query}`);
}
}
// 1. Cannot be used with 'new'
const ArrowClass = () => {};
new ArrowClass(); // TypeError: ArrowClass is not a constructor
// 2. No own 'arguments' object
// Arrow functions inherit 'arguments' from enclosing function scope (if any)
function outer() {
const arrow = () => {
console.log(arguments); // Works! Uses outer's arguments
};
arrow();
}
outer(1, 2, 3); // logs [1, 2, 3]
// But at module/global scope with no enclosing function:
const arrow = () => {
console.log(arguments); // ReferenceError: arguments is not defined
};
// Use rest parameters instead (recommended)
const arrowWithRest = (...args) => {
console.log(args); // Works everywhere!
};
// 3. No own 'super' binding (inherits from enclosing class method if any)
// 4. Cannot be used as generators
// There's no arrow generator syntax - you must use function*
function* generatorFn() { yield 1; } // Works
// () =>* { yield 1; } // No such syntax exists
this is determined at call time — Not when the function is defined, but when it's called. This is called dynamic binding.
5 binding rules in priority order — new binding > explicit binding > implicit binding > default binding (arrow functions are special).
"Left of the dot" rule — In method calls like obj.method(), this is the object immediately left of the dot.
Extracting methods loses this — const fn = obj.method; fn() loses implicit binding. This is the #1 source of this bugs.
call() and apply() invoke immediately — They set this and call the function right away. call takes comma-separated args, apply takes an array.
bind() returns a new function — It permanently binds this for later use. The binding cannot be overridden, even with call or apply.
Arrow functions have no own this — They inherit this from their enclosing scope (lexical binding). Perfect for callbacks.
Arrow functions can't be rebound — call, apply, and bind have no effect on arrow functions' this.
Use arrow class fields for event handlers — handleClick = () => {} ensures this is always the instance, even when extracted.
Strict mode changes default binding — In strict mode, plain function calls have this as undefined, not the global object.
Try to figure out what this refers to in each example before revealing the answer.
const greet = user.greet;
console.log(greet());
```
**Answer:** `"Hi, I'm undefined"`
When `greet` is assigned to a variable and called without an object, implicit binding is lost. Default binding applies, and in strict mode `this` is `undefined`.
increment = () => {
this.count++;
}
}
const counter = new Counter();
const inc = counter.increment;
inc();
inc();
console.log(counter.count);
```
**Answer:** `2`
Arrow function class fields have lexical `this` bound to the instance. Even when extracted, `this` still refers to `counter`.
const alice = { name: "Alice" };
const bob = { name: "Bob" };
const greetAlice = greet.bind(alice);
console.log(greetAlice.call(bob));
```
**Answer:** `"Hello, Alice!"`
Once a function is bound with `bind()`, its `this` cannot be changed — not even with `call()`. The binding is permanent.
console.log(obj.inner.getName());
```
**Answer:** `"Inner"`
With implicit binding, `this` is the object immediately to the left of the dot at call time. That's `obj.inner`, not `obj`.
console.log(calculator.add([1, 2, 3]));
```
**Answer:** `10` (and likely a TypeError in strict mode)
The callback function inside `forEach` has its own `this` (default binding), which is `undefined` in strict mode. The fix is to use an arrow function: `numbers.forEach((n) => { this.value += n; })`.
const double = multiply.bind(null, 2);
console.log(double(5));
console.log(double.length);
```
**Answer:** `10` and `1`
`bind` creates a partially applied function. `double(5)` returns `2 * 5 = 10`. The `length` property of a bound function reflects remaining parameters: `multiply` has 2 params, we pre-filled 1, so `double.length` is 1.