Difficulty: Intermediate
Explain how 'this' works in JavaScript. What are the binding rules and how do arrow functions differ?
The 'this' keyword in JavaScript is determined by how a function is called, not where it is defined. There are four binding rules in order of precedence:
1. new binding: When a function is called with 'new', this refers to the newly created object. 2. Explicit binding: call(), apply(), or bind() explicitly set this. 3. Implicit binding: When a function is called as a method of an object, this refers to that object. 4. Default binding: In non-strict mode, this defaults to the global object (window/global). In strict mode, this is undefined.
Arrow functions are a special case - they do not have their own 'this'. Instead, they inherit 'this' from the enclosing lexical scope at the time they are defined. This makes them ideal for callbacks and event handlers where you want to preserve the outer 'this'.
// 1. Default binding
function showThis() {
'use strict';
console.log(this);
}
showThis(); // undefined (strict mode)
// 2. Implicit binding
const obj = {
name: 'Alice',
greet() {
console.log(`Hello, ${this.name}`);
}
};
obj.greet(); // Hello, Alice
// 3. Explicit binding
const bob = { name: 'Bob' };
obj.greet.call(bob); // Hello, Bob
// 4. new binding
function Person(name) {
this.name = name;
}
const p = new Person('Charlie');
console.log(p.name); // Charlie
Binding precedence: new > explicit (call/apply/bind) > implicit (method call) > default (standalone call).
const user = {
name: 'Alice',
friends: ['Bob', 'Charlie'],
showFriends() {
// 'this' is user here
this.friends.forEach(function(friend) {
// 'this' is undefined/window here (not user!)
console.log(`${this?.name} knows ${friend}`);
});
},
showFriendsFixed() {
// Arrow function inherits 'this' from showFriendsFixed
this.friends.forEach((friend) => {
console.log(`${this.name} knows ${friend}`);
});
}
};
user.showFriends();
console.log('---');
user.showFriendsFixed();
Regular functions in callbacks lose the outer 'this'. Arrow functions solve this by inheriting from the enclosing scope.
const timer = {
seconds: 0,
// Arrow function - inherits 'this' from timer object context
start() {
// 'this' is timer here
const tick = () => {
this.seconds++; // 'this' is still timer
console.log(this.seconds);
};
tick();
tick();
}
};
timer.start();
// Arrow functions cannot be used as constructors
const Foo = () => {};
try {
new Foo();
} catch (e) {
console.log(e.message);
}
// Arrow function 'this' cannot be overridden
const arrowFn = () => console.log(this);
arrowFn.call({ a: 1 }); // ignores call, uses lexical this
Arrow functions have no own 'this', no 'arguments', no 'prototype', and cannot be used with 'new'. Their 'this' is permanently set at creation time.
this, Implicit Binding, Explicit Binding, Arrow Functions, Global Context