bind
! Bind:
The bind() method creates a new function. When the new function is called, the first argument to bind() will be this when it runs, and the subsequent sequence of arguments will be passed as its arguments before the arguments passed. (From MDN)
The bind function has two characteristics:
1. Return a function. 2Copy the code
Returns a mock implementation of the function
Starting with the first feature, let’s take an example:
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
// Returns a function
var bindFoo = bar.bind(foo);
bindFoo(); / / 1
Copy the code
Let’s write the first version of the code:
/ / the first edition
Function.prototype.bind2 = function (context) {
var self = this;
return function () {
returnself.apply(context); }}Copy the code
Return self.apply(context); return self.apply(context);Copy the code
var foo = {
value: 1
};
function bar() {
return this.value;
}
var bindFoo = bar.bind(foo);
console.log(bindFoo()); / / 1
Copy the code
Simulation of parameter transfer
Now the second thing is, you can pass in parameters. This is a little confusing, can I pass in the parameter when I bind? Can I pass the function that bind returns when I execute it? Let's look at an example:Copy the code
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
/ / 1
// daisy
/ / 18
Copy the code
The function requires both name and age. It is possible to pass only one name when bind is called, and another age when the returned function is executed. How can I do that? No hurry, we use arguments to handle:Copy the code
The second edition
/ / the second edition
Function.prototype.bind2 = function (context) {
var self = this;
// Get the bind2 function from the second argument to the last argument
var args = Array.prototype.slice.call(arguments.1);
return function () {
// Arguments refer to the arguments passed in by the function returned by bind
var bindArgs = Array.prototype.slice.call(arguments);
returnself.apply(context, args.concat(bindArgs)); }}Copy the code
A simulated implementation of the constructor effect
With those two things done, here's the hard part! Because there's another thing about BINDCopy the code
A binding function can also use the new operator to create objects: this behavior is like treating the original function as a constructor. The supplied this value is ignored, and the arguments to the call are supplied to the mock function.
That is, when the function returned by bind is used as a constructor, the this value specified by bind is invalidated, but the argument passed in still applies. Here's an example:Copy the code
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'daisy');
var obj = new bindFoo('18');
// undefined
// daisy
/ / 18
console.log(obj.habit); // shopping
console.log(obj.friend); // kevin
Copy the code
! Note that undefind is returned even though value is declared in both global and foo, indicating that the bound this is invalid. If you know the mock implementation of new, you will know that this already refers to obj.
So we can do that by modifying the prototype of the returned function, so let's write that downCopy the code
The third edition
/ / the third edition
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments.1);
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// When used as a constructor, this points to the instance, and the result is true. This points to the instance to get the value from the binding function
// This instanceof fBound? Null: context ', the instance is just an empty object, change null to this, the instance will have habit attribute
// When this refers to the window as a normal function, the result is false, and bind the function's this to context
return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
}
// Change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
fBound.prototype = this.prototype;
return fBound;
}
Copy the code
Optimized implementation of constructor effects
Prototype = this.prototype when we modify fbound. prototype, we will also modify the prototype of the binding function. We can do this with an empty function:Copy the code
The fourth edition
/ / the fourth edition
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments.1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
Copy the code
So far, the big problems have been solved. Give yourself a pat on the back! O (~ ▽ ~) D
Three little questions
1. The apply code is slightly different from the MDN code
Bind (MDN); bind (MDN);
self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))
Copy the code
One more judgment about whether the context exists, and this is wrong!
Here’s an example:
var value = 2;
var foo = {
value: 1.bar: bar.bind(null)};function bar() {
console.log(this.value);
}
foo.bar() / / 2
Copy the code
The above code will normally print 2, if changed the context | | this, this code will print 1! Therefore, the judgment of context should not be carried out here. If you check the English version of the same content in MDN, there is no such judgment! (Updated on March 27, 2018, the Chinese version has been changed to 😀)Copy the code
2. What if you call bind instead of a function?
No, we want to report an error!Copy the code
if (typeof this! = ="function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
Copy the code
3. I need it online
Don't forget to make a compatible:Copy the code
Function.prototype.bind = Function.prototype.bind || function () {
……
};
Copy the code
The final code
Function.prototype.bind2 = function (context) {
//bind -- The object you are trying to bind is not callable
if (typeof this! = ="function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
// Get the bind2 function from the second argument to the last argument
var args = Array.prototype.slice.call(arguments.1);
// An empty function to relay
var fNOP = function () {};
var fBound = function () {
// Arguments refer to the arguments passed in by the function returned by bind
var bindArgs = Array.prototype.slice.call(arguments);
// Consider binding functions that may return values
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
// Change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
Copy the code