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

  1. Function fn inside this equals newChild (instance)
  2. The prototype for the instance is demo.prototype
  3. This outside the function fn points to demo
  4. Prototype = this.prototype (demo.prototype)
  5. So this instanceof fn can be interpreted as newChild instanceof demo.prototype
  6. 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…