Call, Apply, and Bind
In JavaScript, call, apply, and bind are three methods of function objects, all used to explicitly set the this value (i.e., change the context) when a function is invoked. Although their functionality is similar, they differ in how they are called and how arguments are passed.
1.call
syntax:
func.call(thisArg, arg1, arg2, ...)
Features:
- Immediately invokes the function.
- Arguments are passed in as a comma-separated list.
Manual implementation example:
Function.prototype.Call = function(context, ...args) {
// 1. Type check
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2. Handle null/undefined context
if (context == null) {
context = globalThis;
} else {
// 3. Wrap primitive values into objects
context = Object(context);
}
// 4. Create a Symbol to avoid property name collisions with existing keys on `context`
const fn = Symbol();
// 5. Temporarily attach the function (`this`) to `context`
// Here, `this` refers to the function on which `Call` is being invoked
context[fn] = this;
// 6. Execute the function
const result = context[fn](...args);
// 7. Remove the temporarily attached function
delete context[fn];
return result;
};
2.apply
syntax:
func.apply(thisArg, [argsArray])
Features:
- Immediately invokes the function.
- The second argument is an array (or array-like object) used to pass arguments.
Manual implementation example:
Function.prototype.Applay = function(context, args) {
// 1. Type check
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2. Handle null/undefined context
if (context == null) {
context = globalThis;
} else {
// 3. Wrap primitive values into objects
context = Object(context);
}
// 4. Create a Symbol to avoid property name collisions with existing keys on `context`
const fn = Symbol();
// 5. Temporarily attach the function (`this`) to `context`
// Here, `this` refers to the function on which `Applay` is being invoked
context[fn] = this;
// 6. Execute the function, spreading the `args` array as arguments
const result = context[fn](...args);
// 7. Remove the temporary property
delete context[fn];
return result;
};
Tip: When you have a set of arguments stored in an array and want to pass them to a function, apply is very convenient. However, since ES6, you can also use the spread operator with
call:greet.call(person, …['Hi', '.']);
3. bind
syntax:
const newFunc = func.bind(thisArg, arg1, arg2, ...);
Features:
- Does not execute immediately; instead, it returns a new function whose
thisvalue is permanently bound to the specified object. - Supports partial application of arguments (currying / partial function application).
Manual implementation example:
Function.prototype.Bind = function(context, ...contextArgs) {
// 1. Type check
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2. Store the function on which Bind is called
const fn = this;
// 3. Handle null/undefined context
if (context == null) {
context = globalThis;
} else {
// 4. Wrap primitive values into objects
context = Object(context);
}
const boundFunction = function(...args) {
// 5. Determine the `this` value:
// If called with `new`, `this` refers to the newly created instance;
// otherwise, use the bound `context`.
const thisArg = this instanceof fn ? this : context;
return fn.apply(thisArg, contextArgs.concat(args));
};
// 6. If the original function has a prototype, ensure the bound function inherits it
if (fn.prototype) {
boundFunction.prototype = Object.create(fn.prototype);
}
return boundFunction;
};
⚠️ Note: The new function returned by
bindwill ignore any subsequent attempts to change itsthisvalue usingcallorapply, because itsthishas already been permanently (hard) bound.
Comparison Summary
| Method | Executes Immediately? | Argument Format | Return Value | Primary Use Case |
|---|---|---|---|---|
call | ✅ Yes | Individual arguments | Result of execution | Temporarily change this and invoke the function |
apply | ✅ Yes | Arguments as an array | Result of execution | Invoke with this changed, when args are in an array |
bind | ❌ No | Individual args (can be partially applied) | New bound function | Create a new function with a fixed this context |
Real-World Use Cases
- Borrowing methods: e.g., converting array-like objects to real arrays using
Array.prototype.slice.call(arguments). - Event handling: preserving the correct
thisin callbacks, such asbtn.addEventListener('click', handler.bind(this)). - Function currying: pre-filling some arguments with
bindto create more specialized functions.
Reflection Questions (a.k.a. “Looking for trouble”)
Question 1:
Why does the implementation of bind need to consider prototype chain inheritance, while call and apply do not?
Question 2:
Why can’t the this value of a function returned by bind be overridden by subsequently using call or apply?