Skip to content

Bound Method or Callback

Runtime artifact — instantiates Definition-Time Context Binding

Packages a function together with the specific receiver it was taken from, so a later call runs against that object instead of whatever code happens to invoke it.

A Bound Method or Callback is a callable value that has already been fused to the object it belongs to. Take account.deposit and hand it off: an unbound reference would later resolve its receiver — self, this — from whatever code eventually calls it, while a bound method freezes the receiver to account at the moment you take the reference. Its defining move is deliberately narrow: it captures exactly one slice of the definition site — the receiver — and nothing else. That is what separates it from its heavier cousins. A lexical closure drags along every free variable in scope, and a partial application fixes ordinary arguments; a bound method binds only the "which object is this a method of," and lets every other name resolve normally at call time. It is the smallest possible instance of the whole pattern: one binding, taken once, carried with the callable.

Example

A settings panel in a browser app wires up its Save button. The naive line saveButton.onclick = panel.save looks right and fails at runtime: when the button is clicked, the event system calls save with this pointing at the button element (or undefined in strict mode), so this.formValues is missing and the save silently does nothing. The fix is to bind the receiver at wiring time: saveButton.onclick = panel.save.bind(panel). Now the handler is a distinct callable value whose receiver is permanently panel. However the event machinery invokes it — on click, on keyboard shortcut, replayed from a test harness — save still operates on the panel it was defined against. The bug and its fix are the mechanism in one line: the callback stopped inheriting its receiver from the caller and started carrying its own.

How it works

  • At bind time, wrap the function in a new callable that pins the receiver (and, optionally, a few leading arguments) to fixed values.
  • On invocation, self/this is redirected to the captured receiver; all other free names still resolve in the ordinary call-time way.
  • The choice is eager and early: the receiver is decided when the bound value is created, not re-resolved on each call — the deliberate opposite of dynamic dispatch, which looks the receiver up every time.

Tuning parameters

  • What gets bound — receiver only, or receiver plus some leading arguments. Binding more freezes more context but leaves less to vary at the call site (and shades toward partial application).
  • Bind once vs. re-bind per use — cache one bound value and reuse it, or produce a fresh binding each time. Reuse is cheap and stable but pins the receiver even when it should have moved on.
  • Strong vs. weak receiver hold — whether the bound callable keeps its receiver alive. A strong hold guarantees the receiver exists when called; a weak hold avoids leaks but can go stale.
  • Signature adaptation — whether the bound callback is reshaped to the caller's expected arity (e.g. swallowing the event argument a listener passes).

When it helps, and when it misleads

Its strength is being the lightest binding there is: one receiver, captured once, making a callback safe to pass across module and framework boundaries without its self decaying to whoever holds it. The honest failure mode is the mirror image of that hold — a long-lived binding (an event listener that is never removed) keeps its captured receiver reachable and keeps invoking it long after the receiver should be dead, the classic lapsed-listener leak.[1] The classic misuse is binding eagerly to a receiver that ought to be chosen fresh on each call, freezing yesterday's object into today's behavior. The discipline is to pair every long-lived binding with a removal, and to bind a receiver only when it genuinely should be fixed at definition — otherwise leave it to resolve at call time.

How it implements the components

  • behavior_unit — the bound callable is the unit of behavior being carried across contexts.
  • definition_context_capture — it captures one piece of the definition site, the receiver, at the moment the reference is taken.
  • binding_time_policy — it embodies an early binding decision: the receiver is settled when the value is created, not resolved at call.

It does not enumerate or ship a broader captured environment — that is Closure Serialization operating over a lexical closure — nor does it set a general reference-identity or resolution policy, which belongs to Dependency Handle Registry.

Notes

A bound method and a one-argument partial application are nearly the same construct; the distinction is intent — fixing the receiver so the method knows what it is a method of, versus fixing an ordinary parameter. Frameworks that offer both (bind vs. currying) let the line blur, which is why the "what gets bound" dial is worth naming explicitly.

References

[1] The lapsed-listener problem — an observer, often a bound callback, that is registered but never unregistered, so the subject keeps it (and its captured receiver) reachable indefinitely. A standard source of memory leaks in event-driven systems and the reason a long-lived bound callback needs a paired removal.