this Binding: All 5 Rules — Complete & Final
Same function, 4 different calls, 4 different this values. But 5 rules solve it all.
When a function is called standalone — just f() with no object, no new, no bind — this falls back to the default binding.
In sloppy mode (non-strict): this = globalThis (which is window in browsers).
In strict mode: this = undefined.
This is the fallback when no other rule applies. Strict mode exists partly to prevent accidental global pollution via this — if you accidentally write this.x = 5 in sloppy mode, you just created a global variable!
function showThis() {
console.log(this);
}
// Sloppy mode (non-strict)
showThis(); // window (global object)
// Strict mode
(function() {
'use strict';
function strictShowThis() {
console.log(this);
}
strictShowThis(); // undefined
})();
Call bytecode with undefined as the receiver argument. In sloppy mode, V8's built-in Call implementation coerces an undefined receiver to the global object before entering the function body. In strict mode, it passes undefined directly — no coercion.When a function is called as a method on an object — obj.fn() — this = obj (the "receiver"). Only the LAST object in the chain matters: a.b.fn() → this = b.
CRITICAL: Implicit binding is lost if you assign the method to a variable: const fn = obj.fn; fn(); → this = default.
Callbacks also lose implicit binding: setTimeout(obj.fn, 1000) → this = default (or timer object in some old browsers).
const user = {
name: 'Sai',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet(); // 'Hello, Sai' — Implicit binding: this = user
// LOSS OF BINDING — extracting the method
const greetFn = user.greet;
greetFn(); // 'Hello, undefined' — Default binding! this is not user
// LOSS IN CALLBACKS
// setTimeout(user.greet, 1000); // 'Hello, undefined' — lost!
// FIX 1: Arrow wrapper
setTimeout(() => user.greet(), 1000); // 'Hello, Sai' ✅
// FIX 2: .bind()
setTimeout(user.greet.bind(user), 1000); // 'Hello, Sai' ✅
const greetFn = user.greet, you extract the function value from the object. The variable greetFn holds just the function — no reference to user. When called as greetFn(), there is no object before the dot, so the default binding rule applies instead.Sometimes you need to force what this should be. That is explicit binding.
fn.call(thisArg, arg1, arg2): Calls fn immediately with this = thisArg.
fn.apply(thisArg, [argsArray]): Same as call, but arguments as array.
fn.bind(thisArg): Returns a NEW function permanently bound to thisArg. Can be called later.
bind allows partial application (fixing some arguments). Calling .bind() a second time on an already bound function does NOT change this. Once bound, it is locked.
function introduce(greeting, punctuation) {
console.log(greeting + ', I am ' + this.name + punctuation);
}
const user1 = { name: 'Sai' };
const user2 = { name: 'Ali' };
// .call() — immediate, individual args
introduce.call(user1, 'Hello', '!'); // 'Hello, I am Sai!'
introduce.call(user2, 'Namaste', '.'); // 'Namaste, I am Ali.'
// .apply() — immediate, args as array
introduce.apply(user1, ['Hey', '??']); // 'Hey, I am Sai??'
// .bind() — returns new function, can be called later
const boundIntroduce = introduce.bind(user1, 'Hi'); // partial application
boundIntroduce('!!!'); // 'Hi, I am Sai!!!'
// Double bind fails to change this
const rebind = boundIntroduce.bind(user2);
rebind(); // 'Hi, I am Sai!!!' — still user1! bind is permanent.
.bind() a second time on an already bound function does NOT change this. Once bound, it is locked. The spec explicitly prevents rebinding. You can still add more fixed arguments with a second bind, but the this value is permanent.Using new before a function call creates a brand new object. this inside the constructor = that new object.
The new object is implicitly returned unless the constructor explicitly returns another object.
Priority: new overrides bind and implicit binding.
Arrow functions CANNOT use new (no [[Construct]] slot).
function User(name) {
// 'this' = brand new empty object created by 'new'
this.name = name;
this.isAdmin = false;
// implicitly returns 'this'
}
const sai = new User('Sai');
console.log(sai.name); // 'Sai'
console.log(sai.isAdmin); // false
// Constructor returning an object overrides 'this'
function WeirdUser(name) {
this.name = name;
return { message: 'I am not this!' }; // explicitly returns different object
}
const weird = new WeirdUser('Ali');
console.log(weird.name); // undefined — 'this' was discarded!
console.log(weird.message); // 'I am not this!'
.prototype. 3) this is bound to that new object. 4) The constructor body runs. 5) If the constructor returns a non-object (or nothing), the new object is returned. If it returns an object, that object is returned instead — the new this is discarded.Arrow functions ignore ALL 4 previous rules. this is lexically bound at definition time — it captures this from its enclosing scope.
Priority order from highest to lowest:
1. new (overrides everything)
2. Explicit (call / apply / bind)
3. Implicit (obj.fn())
4. Default (standalone call)
5. Arrow (ignores all, uses enclosing scope)
// PRIORITY DEMO
function Person(name) {
this.name = name;
}
const obj = { name: 'Wrong' };
// Explicit bind
const boundPerson = Person.bind(obj);
boundPerson('Bound');
console.log(obj.name); // 'Bound' — Explicit won
// new overrides Explicit bind!
const instance = new boundPerson('New Instance');
console.log(instance.name); // 'New Instance' — new won!
console.log(obj.name); // 'Bound' — obj was NOT modified
// Arrow ignores everything
const arrowPerson = (() => this).bind({ x: 1 });
console.log(arrowPerson()); // global/undefined — bind failed!
new > explicit > implicit > default. Arrows are a separate category — they never participate in the priority game, they just reach into their enclosing scope.Lo kar liya — Key Points:
- ✅ Rule 1: Default binding — standalone call,
this= globalThis (sloppy) or undefined (strict) - ✅ Rule 2: Implicit binding —
obj.fn(),this= obj. LOST if extracted or passed as callback - ✅ Rule 3: Explicit binding —
call/apply/bindforcethis.bindcreates permanently bound function - ✅ Rule 4:
newbinding — creates new object,this= that object, overrides bind - ✅ Rule 5: Arrows — no own
this, ignores all 4 rules, uses enclosing scope'sthis - ✅ Priority:
new> explicit > implicit > default. Arrows exist outside this hierarchy
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login