In the
previous post I described that
this in javascript is not bound to an object, but depends on the context of the call. In practice, it is often necessary that the inside of a function always refers to a specific object.
In this article we will consider two approaches for solving this problem.
1.
jQuery.proxy - approach using the popular jQuery library
2.
Function.prototype.bind is an approach added in JavaScript 1.8.5. We will also consider its use for currying (partial application of the function) and some subtleties of work that units know about.
Introduction
Consider a simple object containing a property
x and a method
f that outputs the value of
this.x to the console
.var object = { x: 3, f: function() { console.log(this.x); } }
As I indicated in the
previous post , when calling
object.f () , the number 3 will be displayed in the console. Suppose now that we need to output this number in 1 second.
setTimeout(object.f, 1000);
Using the wrapper function every time is inconvenient. We need a way to bind the context of the function, so that
this within the
object.f function always
refers to
object1. jQuery.proxy
jQuery.proxy(function, context); jQuery.proxy(context, name);
It's no secret that jQuery is a very popular javascript library, so first we will look at using jQuery.proxy to bind context to a function.
jQuery.proxy returns a new function that, when invoked, calls the original function
function in the
context . Using jQuery.proxy, the above task can be solved like this:
setTimeout($.proxy(object.f, object), 1000);
If we need to specify the same callback several times, instead of duplicating
setTimeout($.proxy(object.f, object), 1000); setTimeout($.proxy(object.f, object), 2000); setTimeout($.proxy(object.f, object), 3000);
it is better to bring the result of $ .proxy to a separate variable
var fn = $.proxy(object.f, object); setTimeout(fn, 1000); setTimeout(fn, 2000); setTimeout(fn, 3000);
')
Let us now pay attention to the fact that we have twice specified the
object inside
$. Proxy (the first time the object's method is
object.f , the second is the passed context,
object ). Maybe there is a possibility to avoid duplication? The answer is yes. For such cases, an alternative possibility of passing parameters is added to $. Proxy - the first parameter must be an object, and the second is the name of its method. Example:
var fn = $.proxy(object, "f"); setTimeout(fn, 1000);
Note that the name of the method is passed as a string.
2. Function.prototype.bind
func.bind(context[, arg1[, arg2[, ...]]])
Let's move on to reviewing Function.prototype.bind. This method was added in JavaScript 1.8.5.
Browser CompatibilityFirefox (Gecko): 4.0 (2)
Chrome: 7
Internet Explorer: 9
Opera: 11.60
Safari: 5.1.4
Emulation Function.prototype.bind from the Mozilla Developer Network Function.prototype.bind = function (oThis) { if (typeof this !== "function") {
Function.prototype.bind has 2 functions - static context binding to a function and partial application of the function.
Basically, bind creates a new function that calls func in context. 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 of the new function.
2.1. Context binding
Using bind to bind context is very simple, just consider an example:
function f() { console.log(this.x); } var bound = f.bind({x: 3});
Thus, the example from the introduction can be written as follows:
var object = { x: 3, f: function() { console.log(this.x); } } setTimeout(object.f.bind(object), 1000);
2.2. Partial application of functions
For simplicity, let's take a look at an example of using bind to partially apply functions.
function f(x, y, z) { console.log(x + y + z); } var bound = f.bind(null, 3, 5);
As you can see from the example - the essence of the partial application of functions is simple - creating a new function with a reduced number of arguments, by “fixing” the first arguments with the bind function.
This would end the article, but ... Functions obtained using the
bind method have some peculiarities in behavior.
2.3. Bind features
In the comments to the previous article 2 examples were given concerning bind (
one ,
two ).
I decided to make a mix of these examples, simultaneously changing the string values to make it easier to deal with them.
Example (try to guess the answers)
function ClassA() { console.log(this.x, arguments) } ClassA.prototype.x = "fromProtoA"; var ClassB = ClassA.bind({x : "fromBind"}, "bindArg"); ClassB.prototype = {x : "fromProtoB" }; new ClassA("callArg"); new ClassB("callArg"); ClassB("callArg"); ClassB.call({x: "fromCall"}, 'callArg');
AnswersfromProtoA ["callArg"]
fromProtoA ["bindArg", "callArg"]
fromBind ["bindArg", "callArg"]
fromBind ["bindArg", "callArg"]
Before you analyze - I will list the main features of bind in accordance with the
standard .
2.3.1. Internal properties
Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]] and [[Scope]] internal properties.
This restriction distinguishes the built-in implementation of
bind from manually defined methods (for example, a
variant from MDN )
2.3.2. call and apply
The behavior of the call and apply methods differs from the standard behavior for functions, namely:
boundFn.[[Call]] = function (thisValue, extraArgs): var boundArgs = boundFn.[[BoundArgs]], boundThis = boundFn.[[BoundThis]], targetFn = boundFn.[[TargetFunction]], args = boundArgs.concat(extraArgs); return targetFn.[[Call]](boundThis, args);
The code shows that
thisValue is not used anywhere. Thus, it is
impossible to change the call context for functions obtained using Function.prototype.bind using
call and
apply !2.3.3. In the constructor
In the constructor,
this refers to the new (created) object. In other words, the context specified with
bind is simply ignored. The constructor calls the
normal [[Call]] of the source function .
Important! If there is no
return this in the constructor, then the return value is generally undefined and depends on the return value of the new function!
Case study
function ClassA() { console.log(this.x, arguments) } ClassA.prototype.x = "fromProtoA"; var ClassB = ClassA.bind({x : "fromBind"}, "bindArg"); // 2.3.1, built-in Function.prototype.bind // bind (, MDN) ClassB.prototype = {x : "fromProtoB" }; // - bind // : fromProtoA ["callArg"] new ClassA("callArg"); // 2.3.3 - this . bind bindArg, // : fromProtoA ["bindArg", "callArg"] // bind : fromBind ["bindArg", "callArg"]. new ClassB("callArg"); // bind , {x : "fromBind"}, bindArg ( bind), - "callArg" // : fromBind ["bindArg", "callArg"] ClassB("callArg"); // 2.3.2. , call , bind . // : fromBind ["bindArg", "callArg"] ClassB.call({x: "fromCall"}, 'callArg');
Conclusion
In this post, I tried to describe the main methods of binding context to functions, and also described some features in the work of Function.prototype.bind, while I tried to leave only the important details (from my point of view).
If you notice any errors / inaccuracies or want to clarify / add something - write in the LAN, correct