The description of the bind
MDN’s description of BIND
Using f.bind(someObject) creates a function with the same body and scope as f, but in this new function, this is permanently bound to the first argument of bind, regardless of how the function is called.
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + this.y;
}
let obj = {
x: 10.y: 20
}
let fun = Point.prototype.toString.bind(obj);
fun(); / / 30
fun(1.2) / / 30
obj.x = 1; obj.y = 2;
fun() / / 3
Copy the code
Fun (1, 2) returns 30, proving that this is permanently bound to obj
Call bind with new
When the function returned by bind is called as a constructor by the new keyword, the this value specified in bind is invalidated, but the argument passed in remains valid.
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + this.y;
}
let obj = {
x: 10.y: 20
}
let YAxisPoint = Point.bind(obj, 0); // Arguments are useful, but
let p = new YAxisPoint(5);
p.toString() / / 5
Copy the code
Polyfill implementation of bind
Directly put the code, using higher-order functions and closures
Function.prototype._bind = function() {
const target = this; // save the original function
let context = arguments[0].// The object to be bound
args = Array.prototype.slice.call(arguments.1); // Get the other parameters of bind
let bound = function() {
let bindArgs = Array.prototype.slice.call(arguments);
// If called as a constructor, this points to the instance object
// We must return the target function
return target.apply(this instanceof target ? this : context, args.concat(bindArgs));
}
// Change the return function's prototype to the binding function's prototype, and the instance can inherit the binding function's prototype
// The prototype object is a reference type. If prototype is assigned directly, modifying the subclass or parent class will affect each other
if (target.prototype) {
var Empty = function() {};
Empty.prototype = target.prototype;
bound.prototype = new Empty(); // bound.prototype.__proto__ === target.prototype / true
Empty.prototype = null;
}
return bound;
}
Copy the code
Is used as a constructor by the bound function
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + this.y;
}
let obj = {
x: 10.y: 20
}
let bound = Point._bind(obj, 0); // The argument is useful, but the bound object is invalid
let p = new bound(5);
p.toString() / / 5
Copy the code
Consider the case where the bound function is used as a constructor:
_bind(obj, 0) returns a bound function. So when bound is the constructor, let p = new bound(5). What happened?
According to the MDN definition
Binding functions are automatically adapted to use the new operator to construct a new instance created by the target function. When a binding function is used to build a value, the supplied this is ignored. However, the supplied argument list is still inserted before the argument list at the time of the constructor call.
Let p = new bound(5); let p = new Point(5); let p = new Point(5); So the final effect is let p = new Point(0, 5). In fact, p.tostring () // 5 also verifies this interpretation.
How to achieve this effect
To achieve this effect, look back at the process of the new keyword. A process of new usually consists of the following four steps
- Create an empty object as an instance of the object to be returned.
- Connect the empty object to the constructor’s prototype object;
- Use the empty object as the constructor this context;
- If the function returns no object, return this;
__proto__ = constructive. prototype (obj.__proto__ = constructor. Prototype) So the key is how to equate let p = new bound(5) with let p = new Point(5).
Just set bound.prototype = point. prototype, but there are some problems with that. Suppose bound.prototype adds a method and point. prototype is synchronized, Because Prototype refers to a prototype object, it is a reference type. In fact, the two will influence each other. How to eliminate this influence is to use an Empty function as the intermediate value.
if (target.prototype) {
var Empty = function() {};
Empty.prototype = target.prototype;
bound.prototype = new Empty(); // bound.prototype === target.prototype / true
Empty.prototype = null;
}
Copy the code
This is the inheritance of the stereotype, and without further elaboration, we can actually see it through the above operation
bound.prototype instanceof Point
// true
Copy the code
So far we have achieved our purpose.
New then performs the third step, constructive.apply (obj, arguments), modifying constructor’s this context to obj. In this case, this within bound should refer to obj, the newly created object. We’re calling our bound function, so we need to change the target context to the instance object obj, which is this. target.apply(this, args.concat(bindArgs));
We can use this instanceof target to determine whether this inherits from target and therefore whether to call the function via the new keyword.
return target.apply(this instanceof target ? this : context, args.concat(bindArgs));
Copy the code
At this point, a simple bind implementation is complete.
reference
Developer.mozilla.org/zh-CN/docs/… Developer.mozilla.org/zh-CN/docs/… Github.com/Raynos/func…