Implement Function.prototype.myBind — a from-scratch version of Function.prototype.bind. Given any function, fn.myBind(thisArg, ...preset) returns a new function whose this is permanently fixed to thisArg and whose argument list is prepended with preset before forwarding to the original.
This is the same feature the language uses for partial application — preset some arguments now, supply the rest at call time.
// Installed on Function.prototype.
fn.myBind(thisArg: any, ...preset: any[]): (...later: any[]) => any
// ^ when the returned function is called with `later`, it invokes
// fn.call(thisArg, ...preset, ...later)
function greet(greeting, name) {
return `${greeting}, ${name}! I am ${this.role}.`;
}
const sayHiAsAdmin = greet.myBind({ role: 'admin' }, 'Hi');
sayHiAsAdmin('Ana'); // → "Hi, Ana! I am admin."
// The bound `this` cannot be overridden by .call/.apply on the result.
function whoAmI() { return this.name; }
const boundToA = whoAmI.myBind({ name: 'A' });
boundToA.call({ name: 'B' }); // → "A", not "B"
// Length reflects the remaining (un-preset) arity.
function add(a, b, c) { return a + b + c; }
add.length; // 3
add.myBind(null, 1).length; // 2 (one arg already supplied)
this is locked — once a function is bound, .call, .apply, and re-bind on the result cannot change its this. This is the spec's most-missed detail.length reflects remaining arity — the bound function's length is Math.max(0, fn.length - preset.length).new is out of scope — you do NOT have to support new boundFn(...) correctly. A basic implementation can ignore it. (See Going further in the solution.)fn — return a brand-new function; the original must keep working unchanged.You'll build a function-returning function that locks this, captures a few "preset" arguments now, and merges them with whatever args the caller supplies later.
In JavaScript, this is decided by how you call a function, not by the function itself. obj.method() makes this be obj; setTimeout(method, 0) makes this be undefined (strict) — same function, different this. bind is the escape hatch: it returns a new function whose this is pinned to the value you choose, no matter who calls it later. It also lets you "pre-fill" some arguments — partial application.
A bound function is a small wrapper that remembers three things from the moment you called myBind: the target function, the thisArg, and the preset arguments. When the wrapper is invoked, it doesn't care what this the caller set — it ignores it and uses the captured one.
Arguments work the same way — captured ones come first, call-time ones come second:
The shape is right but the details are wrong. Most candidates start here:
Function.prototype.myBind = function (thisArg, ...preset) {
const fn = this;
return function (...later) {
// BUG: this calls fn with whatever `this` the caller had,
// not the captured `thisArg`. The `thisArg` is unused.
return fn(...preset, ...later);
};
};
It looks plausible — preset and later args are merged correctly. But invoke a method that reads this and it returns undefined:
function whoAmI() { return this.name; }
whoAmI.myBind({ name: 'A' })(); // → throws: cannot read 'name' of undefined
The reason is right there in the bug comment: fn(...) is a plain call, which sets this to undefined in strict mode (or the global object in sloppy mode). The captured thisArg is sitting unused in the closure — a closure is a function that remembers variables from where it was defined. Here, the wrapper function remembers thisArg from the moment myBind ran, but the plain fn(...) call never uses it. We need a way to invoke fn with a specific this — and that's exactly what Function.prototype.apply is for.
Function.prototype.myBind = function (thisArg, ...preset) {
const fn = this; // capture the original — `this` inside myBind IS the function being bound
function bound(...later) {
// apply forwards `thisArg` as the explicit `this` for the invocation.
// The merged list is preset (captured at bind time) followed by later (call-time).
return fn.apply(thisArg, [...preset, ...later]);
}
// Match native bind's `length` contract: remaining arity after preset is consumed,
// never negative. `length` is non-writable on functions by default, so we use
// defineProperty to override it.
Object.defineProperty(bound, 'length', {
value: Math.max(0, fn.length - preset.length),
configurable: true,
});
return bound;
};
module.exports = {};
Three details earn their lines:
fn.apply(thisArg, ...) replaces the plain fn(...). This is what lets the captured thisArg actually win — apply's first argument becomes the invocation's this, overriding whatever the call-site had.Object.defineProperty patches length. Native bind does this too. A plain bound.length = n silently fails because Function.prototype.length is non-writable; defineProperty is the only path that sticks.function declaration, not an arrow. We never read this inside bound, but a named function makes stack traces readable.The this lock is a direct consequence of the closure:
Trace add.myBind(null, 10, 5)(7):
function add(a, b, c) { return a + b + c; }
const addFifteen = add.myBind(null, 10, 5);
addFifteen(7); // → 22
myBind runs with this = add, thisArg = null, preset = [10, 5]. We capture fn = add and build bound. add.length is 3, so bound.length = max(0, 3 - 2) = 1. Return bound.addFifteen(7) invokes bound with later = [7].bound, we compute [...preset, ...later] = [10, 5, 7].add.apply(null, [10, 5, 7]) — equivalent to add(10, 5, 7).add returns 10 + 5 + 7 = 22.The lock-this case: bound.call({ name: 'B' }) sets the wrapper's this to { name: 'B' }, but inside the wrapper we never read this — we read thisArg from the closure and pass that to apply. The caller's { name: 'B' } is dropped on the floor.
Complexity: each call is O(p + L) for the spread (preset length + later length). Bind itself is O(1).
fn(...) loses this — every implementation that "almost works" but breaks methods has this bug. Always go through apply (or call), passing thisArg explicitly. Example: function fn() { return this.x } fn.myBind({x:1})() should be 1, not throw.bound.length = ... — length is non-writable. If you write bound.length = 2, the assignment silently fails (no error in sloppy mode, a TypeError in strict). Read bound.length back and you get 0 — the wrapper's own arity from function bound(...later). Test fails: expected 2, got 0. Only Object.defineProperty(bound, 'length', { value: 2 }) actually sticks.whoAmI.myBind({name:'A'}).myBind({name:'B'})() returns 'A'. The second myBind wraps the already-bound wrapper; that wrapper still calls the original with the original thisArg. This is correct behavior and matches native bind.(() => this.x).myBind({x:1})() does not set this to {x:1} because arrow functions don't have their own this. We don't need to do anything special — apply's thisArg is silently ignored by arrows. Just don't be surprised.null/undefined as thisArg — in strict mode, these stay as-is; in sloppy mode, they get replaced with the global object. Native bind has the same quirk. Our implementation just passes them through to apply, which is correct.new boundFn(...) — the real spec says new on a bound function should ignore the captured thisArg and use the freshly-constructed object instead. Detect this with new.target inside the wrapper; if it's set, use Reflect.construct(fn, [...preset, ...later], new.target). About 4 extra lines.boundFn.name — native bind sets bound.name = 'bound ' + fn.name. One more defineProperty call patches this.curry(fn) that returns nested single-arg functions until fn.length args have accumulated is a small extension on top of the same closure trick.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
Implement Function.prototype.myBind — a from-scratch version of Function.prototype.bind. Given any function, fn.myBind(thisArg, ...preset) returns a new function whose this is permanently fixed to thisArg and whose argument list is prepended with preset before forwarding to the original.
This is the same feature the language uses for partial application — preset some arguments now, supply the rest at call time.
// Installed on Function.prototype.
fn.myBind(thisArg: any, ...preset: any[]): (...later: any[]) => any
// ^ when the returned function is called with `later`, it invokes
// fn.call(thisArg, ...preset, ...later)
function greet(greeting, name) {
return `${greeting}, ${name}! I am ${this.role}.`;
}
const sayHiAsAdmin = greet.myBind({ role: 'admin' }, 'Hi');
sayHiAsAdmin('Ana'); // → "Hi, Ana! I am admin."
// The bound `this` cannot be overridden by .call/.apply on the result.
function whoAmI() { return this.name; }
const boundToA = whoAmI.myBind({ name: 'A' });
boundToA.call({ name: 'B' }); // → "A", not "B"
// Length reflects the remaining (un-preset) arity.
function add(a, b, c) { return a + b + c; }
add.length; // 3
add.myBind(null, 1).length; // 2 (one arg already supplied)
this is locked — once a function is bound, .call, .apply, and re-bind on the result cannot change its this. This is the spec's most-missed detail.length reflects remaining arity — the bound function's length is Math.max(0, fn.length - preset.length).new is out of scope — you do NOT have to support new boundFn(...) correctly. A basic implementation can ignore it. (See Going further in the solution.)fn — return a brand-new function; the original must keep working unchanged.You'll build a function-returning function that locks this, captures a few "preset" arguments now, and merges them with whatever args the caller supplies later.
In JavaScript, this is decided by how you call a function, not by the function itself. obj.method() makes this be obj; setTimeout(method, 0) makes this be undefined (strict) — same function, different this. bind is the escape hatch: it returns a new function whose this is pinned to the value you choose, no matter who calls it later. It also lets you "pre-fill" some arguments — partial application.
A bound function is a small wrapper that remembers three things from the moment you called myBind: the target function, the thisArg, and the preset arguments. When the wrapper is invoked, it doesn't care what this the caller set — it ignores it and uses the captured one.
Arguments work the same way — captured ones come first, call-time ones come second:
The shape is right but the details are wrong. Most candidates start here:
Function.prototype.myBind = function (thisArg, ...preset) {
const fn = this;
return function (...later) {
// BUG: this calls fn with whatever `this` the caller had,
// not the captured `thisArg`. The `thisArg` is unused.
return fn(...preset, ...later);
};
};
It looks plausible — preset and later args are merged correctly. But invoke a method that reads this and it returns undefined:
function whoAmI() { return this.name; }
whoAmI.myBind({ name: 'A' })(); // → throws: cannot read 'name' of undefined
The reason is right there in the bug comment: fn(...) is a plain call, which sets this to undefined in strict mode (or the global object in sloppy mode). The captured thisArg is sitting unused in the closure — a closure is a function that remembers variables from where it was defined. Here, the wrapper function remembers thisArg from the moment myBind ran, but the plain fn(...) call never uses it. We need a way to invoke fn with a specific this — and that's exactly what Function.prototype.apply is for.
Function.prototype.myBind = function (thisArg, ...preset) {
const fn = this; // capture the original — `this` inside myBind IS the function being bound
function bound(...later) {
// apply forwards `thisArg` as the explicit `this` for the invocation.
// The merged list is preset (captured at bind time) followed by later (call-time).
return fn.apply(thisArg, [...preset, ...later]);
}
// Match native bind's `length` contract: remaining arity after preset is consumed,
// never negative. `length` is non-writable on functions by default, so we use
// defineProperty to override it.
Object.defineProperty(bound, 'length', {
value: Math.max(0, fn.length - preset.length),
configurable: true,
});
return bound;
};
module.exports = {};
Three details earn their lines:
fn.apply(thisArg, ...) replaces the plain fn(...). This is what lets the captured thisArg actually win — apply's first argument becomes the invocation's this, overriding whatever the call-site had.Object.defineProperty patches length. Native bind does this too. A plain bound.length = n silently fails because Function.prototype.length is non-writable; defineProperty is the only path that sticks.function declaration, not an arrow. We never read this inside bound, but a named function makes stack traces readable.The this lock is a direct consequence of the closure:
Trace add.myBind(null, 10, 5)(7):
function add(a, b, c) { return a + b + c; }
const addFifteen = add.myBind(null, 10, 5);
addFifteen(7); // → 22
myBind runs with this = add, thisArg = null, preset = [10, 5]. We capture fn = add and build bound. add.length is 3, so bound.length = max(0, 3 - 2) = 1. Return bound.addFifteen(7) invokes bound with later = [7].bound, we compute [...preset, ...later] = [10, 5, 7].add.apply(null, [10, 5, 7]) — equivalent to add(10, 5, 7).add returns 10 + 5 + 7 = 22.The lock-this case: bound.call({ name: 'B' }) sets the wrapper's this to { name: 'B' }, but inside the wrapper we never read this — we read thisArg from the closure and pass that to apply. The caller's { name: 'B' } is dropped on the floor.
Complexity: each call is O(p + L) for the spread (preset length + later length). Bind itself is O(1).
fn(...) loses this — every implementation that "almost works" but breaks methods has this bug. Always go through apply (or call), passing thisArg explicitly. Example: function fn() { return this.x } fn.myBind({x:1})() should be 1, not throw.bound.length = ... — length is non-writable. If you write bound.length = 2, the assignment silently fails (no error in sloppy mode, a TypeError in strict). Read bound.length back and you get 0 — the wrapper's own arity from function bound(...later). Test fails: expected 2, got 0. Only Object.defineProperty(bound, 'length', { value: 2 }) actually sticks.whoAmI.myBind({name:'A'}).myBind({name:'B'})() returns 'A'. The second myBind wraps the already-bound wrapper; that wrapper still calls the original with the original thisArg. This is correct behavior and matches native bind.(() => this.x).myBind({x:1})() does not set this to {x:1} because arrow functions don't have their own this. We don't need to do anything special — apply's thisArg is silently ignored by arrows. Just don't be surprised.null/undefined as thisArg — in strict mode, these stay as-is; in sloppy mode, they get replaced with the global object. Native bind has the same quirk. Our implementation just passes them through to apply, which is correct.new boundFn(...) — the real spec says new on a bound function should ignore the captured thisArg and use the freshly-constructed object instead. Detect this with new.target inside the wrapper; if it's set, use Reflect.construct(fn, [...preset, ...later], new.target). About 4 extra lines.boundFn.name — native bind sets bound.name = 'bound ' + fn.name. One more defineProperty call patches this.curry(fn) that returns nested single-arg functions until fn.length args have accumulated is a small extension on top of the same closure trick.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.