JavaScript has Function.prototype.bind
that does two different things: it can bind the function's
context to an object, and it can fill arguments with given values from left to right. It helps
to think about these two features separately for clarity.
I say "it can" because each feature can be used independently.
Binding context
JavaScript methods can be passed around, and called separately from the original owner object. For example this works just fine
1 | var calc = { |
What if the method add
inside calc
referenced another property of its owner object? By passing just
the reference to the function, we are losing the context
1 | var calc = { |
The last line gets us a TypeError
return this.add(a, -b);
^
TypeError: Object #<Object> has no method 'add'
The problem is that when we run the function directly subtraction
the value of this
is set to undefined
in the strict mode. Without strict mode, it is set to the global object (window
in the browser).
We could run the function and provide the original object reference manually
1 | console.log(subtraction.call(calc, 5, 4)); // 1 |
This gets tiresome very quickly. Instead of specifying the context at the time of the call, we can bind the context to the function beforehand.
1 | var minus = calc.sub.bind(calc); |
The function minus
has its this
variable set to the calc
object. We could do interesting things,
for example binding the method to another object. As long as it has a method named add
the call should still
work (but the result might not be correct arithmetic!).
1 | var badCalc = { |
When binding a method, we are creating a new function. In the example above, the object's method
invocation calc.sub(10, 1)
still works correctly. It is not affected by binding the context when
creating wrongMinus
function.
Partial application
Another thing Function.prototype.bind
performs is prefilling the arguments from left to right
with given values. Let us create a function that uses our calc
object to increment a number by 1.
1 | var increment = calc.add.bind(calc, 1); |
Notice that we used calc
as the first argument to the bind
call, this is due to bind
signature -
the context is always the first argument. The values to bind follow at position 2, 3, etc.
We can even fill all arguments or provide more values that the method requires (the extra values will be ignored)
1 | var ten = calc.add.bind(calc, 6, 4); |
Keeping the context
If you are happy with the function's current context, and only need to do partial application, you can
pass undefined
or null
as the first argument to the Function.prototype.bind
. For example,
calc.add
works fine by itself
1 | var eleven = calc.add.bind(null, 9, 2); // 11 |
Same with previously bound functions - we can partially apply arguments without knowing and changing the current context.
1 | var minus = calc.sub.bind(calc); |