How to Implement call(), apply(), and bind() Methods in JS
1. call()
Effect: Executes a function immediately and sets the this
context.
Syntax: function.call(thisArg, arg1, arg2, ...)
Example:
js
12345
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const obj = { name: 'Alice' };
greet.call(obj, 'Hello', '!'); // Output: Hello, Alice!
2. apply()
Effect: Similar to call
, but parameters are passed as an array.
Syntax: function.apply(thisArg, [argsArray])
Example:
js
12345
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const obj = { name: 'Bob' };
greet.apply(obj, ['Hi', '!!']); // Output: Hi, Bob!!
3. bind()
Effect: Returns a new function with this
permanently bound.
Syntax: function.bind(thisArg, arg1, arg2, ...)
Example:
js
123456
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const obj = { name: 'Charlie' };
const boundGreet = greet.bind(obj, 'Hey');
boundGreet('!!!'); // Output: Hey, Charlie!!!
Handwritten Implementations
Implementing call()
Steps:
- Verify that the caller is a function.
- Handle the
context
argument (convertnull
orundefined
toglobalThis
). - Assign a temporary function property to
context
. - Execute the function and return the result.
- Clean up the temporary property.
Implementation:
js
12345678910111213
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Caller is not a function');
}
context = context == null ? globalThis : Object(context);
const key = Symbol('temp');
Object.defineProperty(context, key, { value: this, enumerable: false });
try {
return context[key](...args);
} finally {
delete context[key];
}
};
Implementing apply()
Steps:
- Verify the caller is a function.
- Ensure the second argument is an array.
- Handle
context
argument similarly tocall
. - Assign a temporary function property to
context
. - Execute the function with the provided array arguments.
- Clean up the temporary property.
Implementation:
js
12345678910111213141516
Function.prototype.myApply = function (context, args) {
if (typeof this !== 'function') {
throw new TypeError('Caller is not a function');
}
if (!Array.isArray(args)) {
throw new TypeError('The parameter must be an array');
}
context = context == null ? globalThis : Object(context);
const key = Symbol('temp');
context[key] = this;
try {
return context[key](...args);
} finally {
delete context[key];
}
};
Implementing bind()
Steps:
- Verify that the caller is a function.
- Handle the
context
argument. - Store the original function and predefined arguments.
- Return a new function that merges predefined and new arguments.
- Ensure the prototype chain is maintained.
Implementation:
js
123456789101112131415161718192021
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError(
'Function.prototype.myBind - what is trying to be bound is not callable'
);
}
context = context == null ? globalThis : Object(context);
const fn = this;
const bound = function (...innerArgs) {
const allArgs = args.concat(innerArgs);
if (this instanceof bound) {
return new fn(...allArgs);
} else {
return fn.apply(context, allArgs);
}
};
if (fn.prototype) {
bound.prototype = Object.create(fn.prototype);
}
return bound;
};
Summary
-
call()
andapply()
execute functions immediately with a specifiedthis
context
. -
apply()
differs fromcall()
by passing arguments as an array. -
bind()
returns a new function with permanently boundthis
, allowing partial application. - Handwritten implementations use
Symbol
for temporary property names to avoid conflicts.
By understanding and implementing these methods manually, developers can deepen their grasp of JavaScript’s function execution context and scope.