Function.prototype.bind
advancedImplement bind including partial application and the new-operator edge case most candidates miss.
Implement Function.prototype.apply in JavaScript: array-like argument handling, null/undefined thisArg semantics, and the differences from call that interviewers probe.
Implement
Function.prototype.myApply(thisArg, argsArray)so it behaves like the built-inapply— including whenargsArrayisnull,undefined, or an array-like rather than a real array.
This is usually asked immediately after implementing call, and the mechanics are 90% identical. The remaining 10% — how apply treats its arguments array — is exactly what the interviewer is watching, so this article focuses on the differences.
apply shines when the arguments already live in an array (though rest/spread has replaced most uses):
Math.max.apply(null, numbers); // pre-ES6; today: Math.max(...numbers)
fn.apply(this, arguments); // forwarding in old codebases
Reflect.apply(handler, target, args); // the modern equivalent, common in proxiesKnowing that spread made most apply calls obsolete — except when the callee cares about this — is a good aside to offer.
Same temp-Symbol trick as call; the differences are all in argument handling:
Function.prototype.myApply = function (thisArg, argsArray) {
if (typeof this !== "function") {
throw new TypeError(`${this} is not a function`);
}
// apply treats null/undefined argsArray as "no arguments"
if (argsArray == null) {
argsArray = [];
} else if (typeof argsArray !== "object" && typeof argsArray !== "function") {
// Real apply rejects primitives here (spec: CreateListFromArrayLike)
throw new TypeError("CreateListFromArrayLike called on non-object");
}
// Only null/undefined fall back to globalThis; primitives are boxed
const context = thisArg == null ? globalThis : Object(thisArg);
const key = Symbol("myApply");
Object.defineProperty(context, key, {
value: this,
configurable: true,
enumerable: false,
});
try {
// Array.from handles array-LIKES ({ length: 2, 0: 'a', 1: 'b' }),
// which spread alone would reject (spread needs an iterator).
return context[key](...Array.from(argsArray));
} finally {
delete context[key];
}
};function tag(a, b) {
return `${this.name}: ${a}, ${b}`;
}
const arrayLike = { length: 2, 0: "x", 1: "y" };
tag.apply({ name: "ok" }, arrayLike); // "ok: x, y" — native apply accepts it
tag.myApply({ name: "ok" }, arrayLike); // "ok: x, y" — Array.from makes this workA naive context[key](...argsArray) throws here, because spread requires Symbol.iterator and plain array-likes don't have one. The spec operation behind apply (CreateListFromArrayLike) reads length and indexes — Array.from is the closest userland equivalent. Mentioning this distinction is usually the moment the interviewer stops asking follow-ups.
argsArray of null/undefined → call with no arguments (do not throw).argsArray — fn.apply(obj, 42) throws a TypeError natively; silently ignoring it is a spec deviation worth naming.thisArg — fn.myApply(0, []) must box 0, not fall back to globalThis; thisArg == null is the only correct check (see the same trap in call).argsArray — fn.apply(null, "ab") spreads to ('a', 'b'); both Array.from and native behavior agree here since strings are array-like and iterable.try/finally.thisArg — the temp-property trick fails where native apply doesn't; acknowledge the limitation (details in the call article).context || globalThis — rebinds falsy primitives.argsArray directly — breaks on array-likes, the input apply was designed for.null argsArray as an error instead of "no args".apply returns the function's return value.arguments-style loops when Array.from states the intent in one call.| Invokes immediately | Arguments | Returns | |
|---|---|---|---|
call | yes | individually | function's return value |
apply | yes | one array-like | function's return value |
bind | no | partial now, rest later | a new function |
apply accept array-likes at all?" — it predates iterables; arguments was the motivating array-like in 1999.apply today?" — when both this and an argument array vary at runtime; otherwise spread reads better. Reflect.apply is the modern choice in meta-programming code.call using your myApply." — Function.prototype.myCall = function (t, ...a) { return this.myApply(t, a); } — a one-liner that shows you see the relationship.call was marginally faster, but choosing by readability is the correct answer.bind build on this?" — closures over the bound args plus a constructor-call check: implement bind.Implement bind including partial application and the new-operator edge case most candidates miss.
Implement call from scratch: temporary method assignment and why Symbol keys avoid collisions.
Implement myNew(Constructor, ...args): prototype linking, this binding, and the return-object override rule.
Walk the prototype chain by hand, then meet Symbol.hasInstance — and learn where instanceof lies to you.