A function can be bound to an object so that it always has the same this
, regardless of the context of the call.
This is convenient for passing a function as a parameter, so as not to transmit an object reference with it.
For example, consider the loss of context that we encountered in the chapter on timers: calling setTimeout(user.sayHi, 1000)
will run the user.sayHi
function in a global context, does not save this
:
The problem, of course, is not only in the specific id
property, but in the fact that, since this is not passed, you cannot refer to other properties of the object from the method.
The easiest way to get around this is to make a call through a wrapper:
2 | setTimeout( function () { |
But is this the only solution? What now - with each such call to wrap user.sayHi
?
..Of course not. You can bind the context to a function, so that it will always be fixed. And how - we will see
Snap through closure
The easiest way to bind a function to the correct this
is ... Do not use this
!
For example, to access an object from a function via a closure:
Since the function was “weaned off” from this
, it can be safely passed anywhere. The context will be correct.
Modern bind
method
In modern JavaScript, there is a bind method for binding functions. It is supported by most modern browsers, with the exception of IE <9, but is easily emulated.
This method allows you to bind a function to the desired context and even to arguments.
bind
syntax:
var wrapper = func.bind(context[, arg1, arg2...]) |
-
func
- Arbitrary function
-
wrapper
- The wrapper function returned by the
bind
call. It calls func
, fixing the context and, if specified, the first arguments. -
context
- The
wrapper
will call the function with the context this = context
. -
arg1
, arg2
, ... - If the arguments
arg1, arg2...
are specified - they will be added to each call of a new function, and they will be faced with those specified in the call.
The simplest example, we fix only this
:
5 | var user = { name: "Вася" }; |
Use in the constructor to bind the sayHi
method to the object being created:
Importance: 4
Answer: "Вася"
. "Вася"
The first call to f.bind(..Вася..)
returns a "wrapper" that sets the context for f
and passes the call to f
.
The next call to bind
will set the context already for this wrapper, this does not affect anything.
To make this easier to understand, use our own version of bind
instead of the built-in one:
1 | function bind(func, context) { |
3 | return func.apply(context, arguments); |
The code will be as follows:
5 | f = bind(f, {name: "Вася" } ); |
6 | f = bind(f, {name: "Петя" } ); |
Here you can see that the first call to bind
, in line (1)
, returns a wrapper around f
, which looks like this (highlighted):
1 | function bind(func, context) { |
3 | return func.apply(context, arguments); |
In this wrapper, this
is not used anywhere else, only func
and context
. Look at the code, there is no this
anywhere.
Therefore, the next bind
in line (2)
, which is already running on the wrapper and fixes this in it, does not affect anything. What difference does it make as this
in a function that does not use this this
?
[Open task in new window]
bind
with arguments
The bind
method can create a wrapper that captures not only the context, but also a number of arguments.
For example, there is a multiplication function mul(a, b)
:
Based on it, we can create a double
function that will double the values:
6 | var double = mul.bind( null , 2); |
The call to mul.bind(null, 2)
returned a wrapper that captures the context this = null
and the first argument 2
. The context is not used in functions, so it does not matter what it is equal to.
The function is double = mul(2, *)
.
You can also create a triple
, triple value:
var triple = mul.bind( null , 3); |
Creating a new function by fixing the arguments of an existing “scientific” is called currying.
For completeness, consider a combination of both bindings: context and arguments.
Let User
objects have a send(to, message)
method that can send a message to
user. Let's create a function for sending messages to Petya from Vasya. To do this, we need to fix the context and the first argument to send
:
Why such a function may be needed? Well, for example, in order to pass it to setTimeout
or any other place in the program, where it may be that users do not know anything, but need some function of one argument for messages. And this is quite suitable.
bind
cross-browser emulation
For IE <9 and older versions of other browsers that do not support bind
, you can implement it yourself.
Without the support of currying, it's very simple.
Here is our own bind bind
function:
1 | function bind(func, context) { |
3 | return func.apply(context, arguments); |
Its use:
Variant bind
with currying
In order for the bind
function to pass arguments, it needs to be "slightly" complicated:
1 | function bind(func, context ) { |
2 | var bindArgs = [].slice.call(arguments, 2); |
4 | var args = [].slice.call(arguments); |
5 | var unshiftArgs = bindArgs.concat(args); |
6 | return func.apply(context, unshiftArgs); |
Scary looks, right?
If interested, it works like this (in lines):
- Calling
bind
saves additional args
arguments (they come from the 2nd number) to the bindArgs
array. - ... and returns the wrapper
wrapper
. - This wrapper makes the
arguments
array from arguments
and then, using the concat method, adds them to the bindArgs
(3)
arguments. - It then passes the call to
func
(4)
.
The use is exactly as in the example above, only instead of send.bind(admin, visitor)
call bind(send, admin, visitor)
.
Option bind
for methods
Previous versions of bind
bind any function to any object.
But if you want to bind to an object not an arbitrary function, but one that is already in the object, i.e. its method, the syntax can be simplified.
Normal bind
call:
var userMethod = bind(user.method, user); |
Alternative syntax:
var userMethod = bind(user, 'method' ); |
Support for this syntax is easily embedded in a regular bind
. To do this, it suffices to check the types of the first arguments:
Expand Extended Bind Code
In frameworks, as a rule, there are methods of bindings. For example, in jQuery this is $ .proxy, which works as described earlier:
var userMethod = $.proxy(user.method, user); |
var userMethod = $.proxy(user, 'method' ); |
... On the other hand, editors who support autocompletion do not like such “optimizations” very much. Say, if you try to automatically rename a method
, they will be able to find it in the bind(user.method, user)
call bind(user.method, user)
, but they will not be able to in the bind(user, 'method')
.
Total
Summary, shortened, bind
code for binding a function or an object method:
01 | function bind(func, context ) { |
02 | var args = [].slice.call(arguments, 2); |
04 | if ( typeof context == "string" ) { |
05 | args.unshift( func[context], func ); |
06 | return bind.apply( this , args); |
10 | var unshiftArgs = args.concat( [].slice.call(arguments) ); |
11 | return func.apply(context, unshiftArgs); |
Syntax: bind(func, context, аргументы)
or bind(obj, 'method', аргументы)
.
You can also use func.bind from modern JavaScript, if necessary, adding cross-browser emulation by the es5-shim library:
Comments
To leave a comment
Scripting client side JavaScript, jqvery, BackBone
Terms: Scripting client side JavaScript, jqvery, BackBone