Understanding this, call(), apply(), and bind() in JavaScript
Learn how this works and how call(), apply(), and bind() help control it.

Understanding this, call(), apply(), and bind() in JavaScript
This guide explains how this works in JavaScript and how Function.prototype.call, apply, and bind let you control a function’s calling context. Includes practical examples, common pitfalls, and quick reference.
Quick overview
thisis determined by how a function is called, not where it's defined (with important exceptions for arrow functions).call(thisArg, ...args)invokes the function withthisset tothisArgand arguments passed individually.apply(thisArg, argsArray)invokes the function withthisset tothisArgand arguments passed as an array (or array-like).bind(thisArg, ...args)returns a new function permanently bound tothisArg(and optionally pre-filled arguments).
How this is determined (summary)
Global or standalone function call: in non-strict mode
this→ global object (windowin browsers); in strict modethis→undefined.Method call (obj.fn()):
this→obj.Constructor call (
new Fn()):this→ newly created instance.Explicit binding (
call/apply/bind):this→ providedthisArg(with caveats when usingnewwith a bound function).Arrow functions:
thisis lexically inherited from the surrounding (enclosing) scope — cannot be changed bycall,apply, orbind.
Examples
Basic this behavior
function showThis() {
console.log(this);
}
showThis(); // non-strict: global object; strict: undefined
const obj = { x: 10, showThis };
obj.showThis(); // logs obj
new showThis(); // logs the new instance (an object)
call and apply
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // Hello, Alice!
greet.apply(person, ['Hi', '...']); // Hi, Alice...
Use apply when you have an arguments array (or array-like object):
function sum() {
return Array.prototype.reduce.call(arguments, (a,b) => a + b, 0);
}
sum.apply(null, [1,2,3]); // 6
bind for fixed this and partial application
const module = {
x: 42,
getX() { return this.x; }
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // undefined (or global value)
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // 42
// Partial application
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
double(5); // 10
Arrow functions and this
const obj = {
value: 1,
arrow: () => { console.log(this); }, // `this` from surrounding scope, not `obj`
method() {
const inner = () => console.log(this); // inherits `this` from method
inner();
}
};
obj.arrow(); // not obj; likely global or undefined depending on module/strict context
obj.method(); // logs obj
Important details and gotchas
Arrow functions:
call,apply, andbindcannot change an arrow function’sthis. It's lexically fixed to the enclosing scope.bindreturns a new function. The bound function can still be used withnew. When used withnew, thethisArgprovided tobindis ignored; the newly created instance becomesthisfor the original function — but the prototype chain comes from the original function.If
thisArgpassed tocall/apply/bindisnullorundefined, non-strict functions will use the global object; in strict mode,thisremainsnull/undefined.applyis useful for spreading arrays into functions before...spread syntax existed; nowadays you can usefn(...args)instead:fn.apply(obj, args)≈fn.call(obj, ...args)≈fn.apply(obj, args)or simplyfn.call(obj, ...args).
Performance: Historically,
call/applyhad slight overhead; modern engines optimize well. Prefer readability and semantics over micro-optimizing these calls.
Common patterns
- Borrowing methods:
const arrLike = { 0: 'a', 1: 'b', length: 2 };
Array.prototype.slice.call(arrLike); // ['a', 'b']
- Event handlers:
class Button {
constructor() {
this.handleClick = this.handleClick.bind(this); // keep `this` when used as callback
}
handleClick() {
console.log(this); // instance
}
}
- Partial application:
const log = (level, msg) => console.log(level, msg);
const info = log.bind(null, 'INFO');
info('Server started');
Quick comparison
| Method | Runs immediately? | Arguments |
|---|---|---|
call() |
Yes | Passed individually |
apply() |
Yes | Passed as an array (or array-like) |
bind() |
No — returns a new function | Returns a new function; optional pre-filled arguments (partial application) |
Example summary:
fn.call(obj, a, b)
fn.apply(obj, [a, b])
const newFn = fn.bind(obj) // newFn doesn't run immediately
newFn(a, b) // runs with `this` bound to obj
Notes:
bindcan be used for partial application:const double = multiply.bind(null, 2).If a bound function is used with
new, thethisprovided tobindis ignored and the newly created instance becomesthis.Arrow functions inherit
thislexically and cannot have theirthischanged usingcall/apply/bind.
Quick reference
call(thisArg, arg1, arg2, ...)
apply(thisArg, [arg1, arg2, ...])
bind(thisArg, arg1, arg2, ...) → returns new function
When to use:
Use
call/applywhen you want to invoke immediately with an explicitthis.Use
bindwhen you want a function that will always use a specificthis(or pre-fill arguments) later.
Summary
Understanding how this is set in JavaScript is key to writing correct code. Use call and apply to control this for an immediate call, and bind to create a function permanently tied to a specific this. Remember arrow functions inherit this lexically and cannot be re-bound. With these tools you can handle context, borrow methods, and create reliable callbacks and partial functions.
If you want, I can convert this into a shorter cheat-sheet, expand any section with more examples, or produce runnable examples for browser vs Node.js. Which would you prefer?




