Function binding

Pass an object method somewhere else — to setTimeout, an event handler, a .then() — and it tends to break in a confusing way. The method runs, but this no longer points at the object it came from. The property you expected is undefined, or the code throws.

This is the “losing this” problem. Here you’ll see exactly why it happens and the two dependable fixes: a wrapper function, and the built-in bind. Then we’ll push bind further into partial application — fixing arguments ahead of time, not just the context.

Losing “this”

You’ve met this trap already. The moment a method is detached from its object and called on its own, this is gone.

Watch it happen with setTimeout:

let member = {
  handle: "Maya",
  greet() {
    alert(`Welcome, ${this.handle}!`);
  }
};

setTimeout(member.greet, 1000); // Welcome, undefined!

The alert says undefined, not Maya.

The reason is that this in JavaScript is decided by how a function is called, not where it was defined. member.greet is one thing: a reference to a function. The dot that would have set this = member only applies at the call site member.greet(). Pass the bare reference into setTimeout and that dot never happens.

You can make the detachment obvious by copying the reference to a variable first:

let f = member.greet;
setTimeout(f, 1000); // lost member context

f and member.greet point at the same function object. Neither carries member with it.

member
handle: “Maya”
greet: ƒ
— pass member.greet only —>
setTimeout
holds: ƒ (no member)

later: engine calls the function with no object → this.handle is undefined

The method reference travels without its object. When setTimeout finally calls it, there is no dot to supply this.

Where does this actually point then? In a browser, setTimeout calls your function with this = window, so this.handle reads window.handle — which doesn’t exist, hence undefined. Under Node.js this becomes the internal timer object; the exact value differs by environment, but the outcome is the same: not your member. (In cases where the function runs in strict mode with no explicit context, this is simply undefined, and this.handle throws instead.)

The pattern is common: hand an object’s method to some scheduler or framework that will call it later. The question is how to guarantee it still runs with the right context.

Solution 1: a wrapper

The plainest fix is to wrap the call in another function:

let member = {
  handle: "Maya",
  greet() {
    alert(`Welcome, ${this.handle}!`);
  }
};

setTimeout(function() {
  member.greet(); // Welcome, Maya!
}, 1000);

Now it prints Maya. The wrapper is what setTimeout calls, and inside it we call member.greet() with the dot intact. member is reached through the outer lexical environment — the closure — so the method runs on the correct object.

An arrow function makes it terser:

setTimeout(() => member.greet(), 1000); // Welcome, Maya!

This works, but it leaves a small crack. There’s a one-second gap before the timer fires, and the wrapper reads member at fire time, not now. If member gets reassigned in that window, the wrapper faithfully calls whatever member points at then:

let member = {
  handle: "Maya",
  greet() {
    alert(`Welcome, ${this.handle}!`);
  }
};

setTimeout(() => member.greet(), 1000);

// ...the value of member changes within 1 second
member = {
  greet() { alert("Someone else in setTimeout!"); }
};

// Someone else in setTimeout!

The next solution closes that gap by locking onto the object itself.

Solution 2: bind

Every function has a built-in method bind that pins this permanently.

The basic form:

// more complex syntax will come a little later
let boundFunc = func.bind(context);

func.bind(context) returns a new, function-like “exotic object”. Call it and it quietly forwards the call to func with this forced to context. The arguments you pass go straight through.

Think of it as a sealed envelope: func inside, context written on the outside as the return address. No matter who mails it, it reports back to the same place.

let member = {
  handle: "Maya"
};

function func() {
  alert(this.handle);
}

let funcMember = func.bind(member);
funcMember(); // Maya

func.bind(member) is a bound variant of func with this = member welded on. Call funcMember() however you like — the context stays member.

funcMember(“Hi”)
the bound object
—→
func.call(member, “Hi”)
this = member   arg = “Hi”
bind wraps the original function so every call reroutes this to the fixed context. Arguments still flow through untouched.

Arguments pass through as-is:

let member = {
  handle: "Maya"
};

function func(phrase) {
  alert(phrase + ', ' + this.handle);
}

// bind this to member
let funcMember = func.bind(member);

funcMember("Hello"); // Hello, Maya (argument "Hello" is passed, and this=member)

Now the real use case — binding an actual method:

let member = {
  handle: "Maya",
  greet() {
    alert(`Welcome, ${this.handle}!`);
  }
};

let greet = member.greet.bind(member); // (*)

// can run it without an object
greet(); // Welcome, Maya!

setTimeout(greet, 1000); // Welcome, Maya!

// even if the value of member changes within 1 second
// greet uses the pre-bound value which is a reference to the old member object
member = {
  greet() { alert("Someone else in setTimeout!"); }
};

Line (*) takes member.greet and binds it to member. The resulting greet carries its context with it. Call it bare, hand it to setTimeout — the context is always right. And crucially, bind captured the object member referred to at bind time. Reassigning the member variable afterward can’t touch the bound function; it still greets Maya. That’s the difference from the wrapper.

Both buttons below invoke the same method — but one goes through a bare reference and the other through a bound copy. Watch the greeting change:

interactiveDetached call vs bound copy

Arguments still ride along untouched — only this is locked:

let member = {
  handle: "Maya",
  say(phrase) {
    alert(`${phrase}, ${this.handle}!`);
  }
};

let say = member.say.bind(member);

say("Hello"); // Hello, Maya! ("Hello" argument is passed to say)
say("Bye"); // Bye, Maya! ("Bye" is passed to say)

Partial functions

So far bind has only fixed this. It can do more: it can fix leading arguments too. This is used less often, but it’s a handy trick to have.

The full signature:

let bound = func.bind(context, [arg1], [arg2], ...);

Whatever you pass after context becomes the first arguments of every call to the bound function.

Take a currency-conversion function:

function convert(rate, amount) {
  return rate * amount;
}

Bind the rate to 0.9 and you get a toEuros:

function convert(rate, amount) {
  return rate * amount;
}

let toEuros = convert.bind(null, 0.9);

alert( toEuros(100) ); // = convert(0.9, 100) = 90
alert( toEuros(200) ); // = convert(0.9, 200) = 180
alert( toEuros(300) ); // = convert(0.9, 300) = 270

convert.bind(null, 0.9) builds a new function that always calls convert with rate pinned to 0.9. Whatever you pass to toEuros lands in amount.

toEuros(100)
rate = 0.9 fixed
amount = 100 new
→ convert(0.9, 100) = 90
Fixing the first argument with bind. The pre-filled arg sits in front; new arguments queue up behind it.

This is partial application: making a new function by fixing some parameters of an existing one.

Note we don’t use this at all here, yet bind insists on a context argument. So we pass null as a filler.

A toYen follows the same recipe:

function convert(rate, amount) {
  return rate * amount;
}

let toYen = convert.bind(null, 150);

alert( toYen(1) ); // = convert(150, 1) = 150
alert( toYen(2) ); // = convert(150, 2) = 300
alert( toYen(3) ); // = convert(150, 3) = 450

Type a number and watch the two partials specialise the same convert. Only amount is left free; rate was frozen at bind time:

interactivetwo converters from one convert

Why bother making a partial?

Two reasons. First, readability: toEuros and toYen say what they do, and callers never have to remember to pass the fixed first argument. Second, specialization: when you have a very general function, a partial gives you a narrower, more convenient variant.

For example, given a generic send(from, to, text), a member object might expose a partial sendTo(to, text) that always sends from the current member. The from is baked in once.

Going partial without context

Sometimes you want to fix arguments but leave this alone — say, for an object method whose context should still be determined at call time.

Native bind can’t do this. Its first parameter is the context; you can’t skip it to get straight at the arguments.

The fix is a small helper of our own. It’s a few lines:

function partial(func, ...argsBound) {
  return function(...args) { // (*)
    return func.call(this, ...argsBound, ...args);
  }
}

// Usage:
let member = {
  handle: "Maya",
  post(channel, text) {
    alert(`(${channel}) ${this.handle}: ${text}`);
  }
};

// add a partial method with a fixed channel
member.postEmail = partial(member.post, "email");

member.postEmail("On my way");
// Something like:
// (email) Maya: On my way

The wrapper (*) is the key. Because it’s called as member.postEmail(...), its own this is member, and it forwards that same this to func via call. So the context is not frozen — it comes from the call site, exactly what we wanted. Only the arguments are pre-filled.

When member.postEmail("On my way") runs, the wrapper calls member.post with:

  • The same this it received — member — because postEmail was called as a method of member.
  • Then ...argsBound — the arguments captured when partial ran ("email").
  • Then ...args — the arguments handed to the wrapper ("On my way").
member.postEmail(“On my way”)
↓ wrapper forwards via func.call

member.post( this=member // from call site, not fixed
  “email” // argsBound, “On my way” // args )

partial keeps this dynamic (taken from the call site) while pre-pending the bound arguments in front of the new ones.

The demo below drives the point home: a single partial wrapper (emailPost, with the channel pre-filled) is shared by two different members. The bound argument is frozen, but this still comes from whichever object you call it on — click the two buttons and the name changes while the channel stays put:

interactiveOne partial, this still dynamic

The spread syntax makes stitching the two argument lists together almost trivial. There’s also a ready-made _.partial in lodash if you’d rather not roll your own.

Summary

func.bind(context, ...args) returns a bound variant of func with the context this fixed, plus any leading arguments you supply.

The everyday job for bind is pinning this on an object method so you can pass it somewhere that would otherwise strip its context — a scheduler like setTimeout, an event handler, a promise callback. Unlike a wrapper closure, bind captures the object at bind time and shrugs off later reassignment of the variable.

Fixing arguments of an existing function yields a narrower, partially applied function. Partials pay off when one argument is always the same for your task — think send(from, to) where from never changes — so you set it once and stop repeating yourself. When you need to fix arguments but keep this flexible, a tiny partial helper built on call does the job.