Concept: The bind() method creates a new function. When bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments are used as arguments to the new function. Several features of BIND are as follows
- Returns a new function
- Segmented receiving parameter
- Change the direction of this
- A new function returned can use the new operator
Let’s implement these features step by step
Return a new function
Calling bind returns a new function, but the code is still inside the function, with the this pointer changed, so we can verify this
function demo(){
console.log('hello')}var child = demo.bind()
child() // hello
Copy the code
Demo.bind () returns a new function child, which executes internal demo code to print Hello, so it’s all right. Let’s get started
Function.prototype.myBind = function(){
var _t = this
return function(){
_t()
}
}
Copy the code
The first step is to get this to get the original function, and then return a new function that executes the original function logic inside the function, modifying the original demo
function demo(){
console.log('hello')}var child = demo.myBind()
child() // hello
Copy the code
Execute child and print Hello, fine. Now let’s see how to receive parameters in segments.
Segmented receiving parameter
Without further ado, let’s expand the original chestnut
function demo(name, year){
console.log(`hello ${name} ${year}`)}var child = demo.bind(null.'world')
child(2021) // hello world 2021
Copy the code
The first parameter is the new function of this, so skip temporarily, behind again, and then look at this example, bind the second parameter start is a parameter list, when can receive n parameters, implementation of the new function when also can receive n argument list, so we need to receive 2 times into the participation, and pieced together in sequence, the incoming new function
Function.prototype.myBind = function(){
var _t = this
var outArgs = Array.prototype.slice.call(arguments.1) // Get all the arguments starting with the second argument
return function(){
var innerArgs = Array.prototype.slice.call(arguments) // Get all parameters
_t.apply(null, outArgs.concat(innerArgs)) // The parameters are arrays, so use apply}}Copy the code
Change “bind” from “chestnut” to “myBind” and find the same output, so that’s done! Go to the next step to modify the this pointer
This points to the
When we say “change this”, the first thing that comes to mind is “call apply”, so we take a context (this) from the original implementation and use it when we apply it
Function.prototype.myBind = function(context){
var _t = this
var outArgs = Array.prototype.slice.call(arguments.1)
return function(){
var innerArgs = Array.prototype.slice.call(arguments)
_t.apply(context, outArgs.concat(innerArgs))
}
}
Copy the code
Let me write another chestnut to verify that
var obj = {
month : 11
}
function demo(name, year){
console.log(`hello ${name} ${year} The ${this.month}`)}var child = demo.myBind(obj, 'world')
child(2021) // hello world 2021 11
Copy the code
This points to the first argument in myBind, obj, so this.month equals 11. Next comes the new operator, which is the hardest to understand
New operator
There are two special cases of using the new operator
- The first parameter of bind, this, fails; this points to the instance
- The prototype chain inherits the original function
Write a chestnut to verify both cases
var obj = {
name: 'ts'
}
function demo(){
this.age = 100
console.log(this.name)
}
demo.prototype.sayAge = function(){
console.log(this.age)
}
var child = demo.bind(obj)
var newChild = new child() // undefined
newChild.sayAge() / / 100
Copy the code
There’s nothing wrong with the log. So let’s implement it. Since we want to invalidate this, we need to know when to use new and when to make a normal call. If this refers to an instance, then how do we know that this refers to an instance? Let’s write the implementation first and then explain it step by step with the chestnut above
Function.prototype.myBind = function(context){
var _t = this
var outArgs = Array.prototype.slice.call(arguments.1)
var fn = function(){
var innerArgs = Array.prototype.slice.call(arguments)
_t.apply(this instanceof fn ? this : context, outArgs.concat(innerArgs))
}
fn.prototype = this.prototype // Implement prototype inheritance
return fn
}
Copy the code
The following step solution
- Function fn inside this equals newChild (instance)
- The prototype for the instance is demo.prototype
- This outside the function fn points to demo
- Prototype = this.prototype (demo.prototype)
- So this instanceof fn can be interpreted as newChild instanceof demo.prototype
- If the answer is true, then the operation is new, false is a normal call (normally this refers to the window).
Bind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind = myBind
To optimize the
The previous fn.prototype = this.prototype implementation was somewhat crude and could cause problems, such as the demo prototype being modified after new properties or methods were added to the prototype
function demo(){
this.age = 100
}
demo.prototype.sayAge = function(){
console.log(this.age)
}
var child = demo.myBind()
var newChild = new child()
newChild.sayAge()
// Add a prototype method
newChild.__proto__.sayHello = function(){
console.log('hello')
}
newChild.sayHello() // hello
// The demo prototype also has this method
demo.prototype.sayHello() // hello
Copy the code
To solve this situation, we can use primitive inheritance
Function.prototype.myBind = function(context){
var _t = this
var outArgs = Array.prototype.slice.call(arguments.1)
var Fpop = function() {} // transfer function
var fn = function(){
var innerArgs = Array.prototype.slice.call(arguments)
_t.apply(this instanceof Fpop ? this : context, outArgs.concat(innerArgs))
}
Fpop.prototype = this.prototype // Implement prototype inheritance
fn.prototype = new Fpop()
return fn
}
Copy the code
Knock it off!! Original link: github.com/mqyqingfeng…