role
The bind() method creates a new function, and when bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function.
Look at the code:
// Assign the write method of document
let altwrite = document.write
altwrite('hellow') // Uncaught TypeError: Illegal invocation
Copy the code
This points to a Document object, assigned to Altwrite, which executes without a call. This points to a Global or window object.
If you want this of the Altwrite method to also point to a document object, you can also use CAL () and apply() described earlier.
// Assign the write method of document
let altwrite = document.write
altwrite.bind(document) ("hello")
altwrite.call(document."hello")
Copy the code
Bind () returns a function that points this in Altwrite to the document object and passes in arguments to execute; CAL () points this in Altwrite to the Document object, which passes in the argument directly using the Altwrite method and returns the executed value.
CAL (), apply(), bind()
1. Similarities:
-
Both are used to change the direction of the function’s this object;
-
The first argument is the object to which this refers, that is, the context that you want to specify;
-
Both can be passed with subsequent arguments, the same as CAL () and bind().
2. The difference between:
-
Bind () returns the corresponding function for later call; CAL () and apply() are called immediately and return the value after the function is executed.
-
CAL () and bind() do not take the first argument; the method’s this is bound as a global object. In strict mode, the value of this will be undefined.
Bind () If the argument list of bind is empty, or thisArg is null or undefined, this executing the scope will be treated as the first argument to the new function.
-
Bind () constructs the binding function using the new operator and ignores the first argument.
The instance
The bind() method can also be used where CAL () is used, as above, but bind() returns functions and has its own use.
1. Bind functions
-
The simplest use of bind() is to create a function that has the same value no matter how it is called. A common mistake, like the example above, is to take a method out of an object and call it, expecting this to point to the original object.
let n = 0 let a = { n: 1.getN: function() { console.log(this.n) } } let b = { n: 2 } a.getN() / / 1 let getNc = a.getN getNc() // 0 this points to the global object let getNcc = a.getNc.bind(a) getNcc() / / 1 b.getNcc() // 1 This refers to the first argument of bind Copy the code
-
Before ES6, we used _this, that, self, etc., to save this and refer to it after changing the context
let a = { n: 1.getN: function() { let _this = this $('.xx').on('click'.function (event) { console.log(_this.n) / / 1}}})Copy the code
This can be avoided by using the arrow function in ES6, which is the first method to use.
$('.xx').on('click'.function (event) { console.log(this.n) / / 1 }).bind(this)) Copy the code
2. Cooperate with setTimeout
This is similar to the second one above. In general, setTimeout() ‘s this points to a window or global object.
function Person (name) {
this.name = name
}
// Call getInfo after 1 second
Person.prototype.print = function () {
window.setTimeout(this.getInfo.bind(this), 1000)
}
Person.prototype.getInfo = function () {
console.log('My name is' + this.name)
}
Copy the code
3. The partial functions
Another simplest use of bind() is to make a function have default initial arguments. Just write these arguments (if any) after this as arguments to bind(). When the binding function is called, these parameters are inserted at the beginning of the argument list of the target function, followed by the parameters passed to the binding function.
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2
}
var list1 = list(1.2.3); / / [1, 2, 3]
var result1 = addArguments(1.2); / / 3
// Create a function with a list of default arguments.
var leadingThirtysevenList = list.bind(null.37);
// Create a function with a default first argument
var addThirtySeven = addArguments.bind(null.37);
var list2 = leadingThirtysevenList();
/ / [37]
var list3 = leadingThirtysevenList(1.2.3);
// [37, 1, 2, 3]
var result2 = addThirtySeven(5);
// 37 + 5 = 42
var result3 = addThirtySeven(5.10);
// 37 + 5 = 42, the second argument is ignored
Copy the code
4. Bind functions as constructors
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.
What do you mean? Let’s start with a chestnut:
function Person (name, age) {
this.name = name
this.age = age
this.getInfo = function () {
console.log(this.name + this.age)
}
}
let p1 = new Person('xuxu'.18)
p1.getInfo() // xuxu18
Copy the code
Use the bind () :
let obj = {
age: 19
}
let newPerson = Person.bind(obj, 'x')
let p2 = new newPerson(5)
p2.getInfo() // x5
Copy the code
According to the use of bind(), newPerson’s this points to obj, but the supplied this is ignored when constructed with the new new operator, so this points to P2.
5. A shortcut
Bind () can also create shortcuts for functions that require a particular this value.
Take the example of an array-like object converted to an array, of course, using the ES6 extension described earlier is the best
Array.prototype.slice.call(arguments)
Copy the code
But if you have a lot of array-like objects, you’ll need to write a lot of these expressions, so you can use bind()
let slice = Function.prototype.call.bind(Array.prototype.slice)
slice(arguments)
Copy the code
Code implementation (pre-ES6)
-
Let’s start by saying that this is implemented using the pre-ES6 method, which simplifies things a little bit.
-
Use call() and apply(), and if you don’t use call, implement a call yourself and replace it.
Here’s an example:
let a = b.bind(obj, '1'.'2')
a('3')
Copy the code
The first step
Change this to return a function to be executed later.
-
The first element in arguments is the first argument obj that we pass in when we call bind. Use apply to call a and refer this from A to obj.
Function.prototype.myBind = function () { return this.apply(arguments[0])}Copy the code
-
Since bind does not execute immediately but returns a function, it returns it wrapped in a function. (Arrow functions can be used in ES6)
Function.prototype.myBind = function () { let self = this return function () { return self.apply(arguments[0])}}Copy the code
-
Finally, check whether a is function, and you’ll be perfect
Function.prototype.myBind = function () { if (typeof this! = ='function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this return function () { return self.apply(arguments[0])}}Copy the code
This completes the first step perfectly: change this to return a function
The second step
When you call bind(), you may pass in other arguments ‘1’, ‘2’ in addition to the object to which you want to bind this. The arguments class array object is truncated from obj and converted to an array (ES6 can use […arguments]), and the resulting args is passed to apply.
Function.prototype.myBind = function () {
if (typeof this! = ='function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
let args = Array.prototype.slice.call(arguments.1)
return function () {
return self.apply(arguments[0], args)
}
}
Copy the code
The third step
It is also possible to pass in arguments when using the a function, so you need to merge the passed arguments into the second argument of apply().
When we use function A, it’s actually the function that returns, i.e
return function () {
return self.apply(arguments[0], args)
}
Copy the code
So, just spell the arguments of this function after args. (ES6 is simpler)
Function.prototype.myBind = function () {
if (typeof this! = ='function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this
let thatArgs = arguments[0]
let args = Array.prototype.slice.call(arguments.1)
return function () {
Array.prototype.push.apply(args, Array.prototype.slice.call(arguments))
return self.apply(thatArgs, args)
}
}
Copy the code
At this point, you’ve basically implemented bind in the chestnut above.
The fourth step
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.
This step mainly does this, which means using new to construct a new instance created by the target function
let newPerson = Person.bind(obj, 'x')
console.log(newPerson) F Person() returns the Person function
let p2 = new newPerson()
Copy the code
Ignore obj in bind(), and this points to the instance p2 that was created.
-
As you can see, the only fBound function that returns is the one that called bind(), the Person above, with this and its arguments bound at execution time. By definition, when new, this refers to the created instance, so when the returned fBound is new, this refers to the instance. The returned Person’s this points to the real column, otherwise to obj.
With the new keyword, P2 “inherits” the instance from Person.prototype, so we change fBound’s prototype to Person’s prototype.
Function.prototype.myBind = function () { if (typeof this! = ='function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this let thatArgs = arguments[0] let args = Array.prototype.slice.call(arguments.1) let fBound = function () { Array.prototype.push.apply(args, Array.prototype.slice.call(arguments)) return self.apply( this instanceof fBound ? this : thatArg, args) } fBound.prototype = self.prototype return fBound } Copy the code
-
Prototype (newPerson.prototype = {}); fBound. Prototype (newPerson.prototype = {}) Therefore, we need an intermediate variable fNOP equal to an empty function to maintain the prototype relationship and make fbound. prototype and Person.prototype no longer refer to the same prototype function:
Function.prototype.myBind = function () { if (typeof this! = ='function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}let self = this let thatArgs = arguments[0] let args = Array.prototype.slice.call(arguments.1) let fBound = function () { Array.prototype.push.apply(args, Array.prototype.slice.call(arguments)) return self.apply( this instanceof fBound ? this : thatArg, args) } let fNOP = function () {} if (self.prototype) { fNOP.prototype = self.prototype } fBound.prototype = new fNOP() return fBound } Copy the code
FNOP and Person use the same prototype, and fbound. prototype is an instance of fNOP, whose __proto__ refers to Person.prototype. Therefore, modifying fbound. prototype directly does not change Person’s Prototype.
At the end
There are many other differences between the above implementation of bind() and the actual algorithm. To paraphrase function.prototype.bind (), though there may be other differences, there is no need to list them further.
One last note
-
Bind () to a function, and then bind() again has no effect. When fn is finally called, this is always referred to thisArg from the first bind().
-
After binding this with bind(), you cannot use CAL () and bind() to redirect the function.
reference
- Function.prototype.bind()
- Bind yourself
- The use and implementation of bind() method in Javascript
- Javascript basics call, apply, bind
- JS call, apply, bind methods in detail