Implement a bind function manually!

Original address:Manually implement a bind function! - Zhihu

1.bind function usage

The bind() method is used to create a new function. The first parameter received by this new function represents this. Using the bind() function, I can arbitrarily change the this pointer inside the function.

Explanation from the official website:

The bind() method creates a new function. When bind() is called, the this of this new function is specified as the first parameter of bind(), and the remaining parameters will be used as parameters of the new function for use when calling.

The official website explains it more transparently. In order to let everyone understand the usage of bind more deeply, we will use code to demonstrate it here.

Sample code:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  // 使用bind创建一个新函数
  let newFn = fn.bind(obj, 10, 20, 30);
  // 调用新函数
  newFn();
  // 调用旧函数
  fn(10, 20, 30);
</script>

Output result:

In the above code, we declared a function fn, and printed this and parameters inside the function. Then we used bind() to create a new function, and the first parameter was passed in obj, which means this inside the new function. pointed to obj. Execute two functions respectively. The this point inside the two functions points to the global and the window.

2.Characteristics of bind function

If we want to manually implement a bind function, it is very necessary to understand the characteristics of the bind function, so knowing ourselves and the enemy can win every battle.

From the code in the previous section, we roughly summarized the following characteristics of the bind function:

2.1 Return a new function

The bind function is actually a copy of the original function, and the original function can be processed according to the original logic.

Sample code:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  // 使用bind创建一个新函数
  let newFn = fn.bind(obj, 10, 20, 30);
  console.log(typeof newFn); // 'function'
</script>

2.2 New functions can still continue to pass parameters

The new function created by the bind function can receive parameters. In the previous examples, we passed the parameters in when creating them. In fact, there is no need to pass them.

Sample code:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  let newFn = fn.bind(obj, 10);
  newFn(20, 30);
</script>

Output result:

The above output result is consistent with the result when we directly pass all parameters when creating, and our parameters in the above code are passed separately, that is to say, after using bind to create a new function, when calling the new function, the function receives The parameters are the parameters passed in when calling + the parameters passed in when creating.

2.3 New function as constructor

If we execute the new function created using bind as a constructor, then the point of this will have nothing to do with the binding when bind was created, it will point to a new reference.

Sample code:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  function fn(name) {
    this.name = name;
    console.log("函数内部this指向:", this);
  }
  let newFn = fn.bind(obj);
  let obj2 = new newFn("构造函数");
</script>

Output result:

In the above code, we used bind to create a new function newFn, and pointed the this of this function to obj, but when we used it later, we used the new keyword to create it. At this time, the this point inside the function no longer points to obj, but to obj. It points to fn.

So since this points to fn, after we add attributes or methods to the fn prototype, obj2 can be accessed.

3. Implement the bind function

Now that we know several characteristics of bind, we can implement it by following its characteristics. First of all, it returns a new function. We can set up the shelf first.The code is as follows:

Function.prototype.myBind = function () {
  // 返回新函数
  return function () {
    // 代码先省略
  }
}

The above code is just a basic shelf, we can just fill it with code. Next, we need to point the this of the function to the first parameter passed in, and the new function created using bind can continue to receive parameters.The code is as follows:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 手写bind函数
  Function.prototype.myBind = function (context) {
    const _this = this; // 当前函数
    let args = Array.from(arguments).slice(1); // 将参数列表转化为数组,出去第一个参数外
    // 返回新函数
    return function () {
      // context 是传进来的this
      _this.apply(context, args.concat(Array.from(arguments))); // 利用apply将this指向context,参数进行拼接
    }
  }
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  let newFn = fn.myBind(obj, 10, 20);
  newFn(30);
</script>

There are two points to note in the above code. The first point is to use the apply function to point the this of the function to the passed context. The second point is to splice the newly passed parameters with args, because when we call newFn, it may New parameters are passed in, so the old and new parameters need to be spliced ​​together.

Output result:

The above output result is the same as the result output by directly using the bind function. The above code is not perfect enough. If we execute the new function created as a constructor, the execution of this is not consistent with the native bind.

code show as below:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 手写bind函数
  Function.prototype.myBind = function (context) {
    const _this = this; // 当前函数
    let args = Array.from(arguments).slice(1); // 将参数列表转化为数组,除去第一个参数外
    // 返回新函数
    return function () {
      // context 是传进来的this
      _this.apply(context, args.concat(Array.from(arguments))); // 利用apply将this指向context,参数进行拼接
    }
  }
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  let newFn = fn.myBind(obj, 10, 20); // 调用封装的bind
  let newFn1 = fn.bind(obj, 10, 20); // 调用原生的bind


  new newFn("myBind构造函数");
  new newFn1("bind构造函数");
</script>

Output result:

The above output results are inconsistent, indicating that if a new function created using native bind is executed using a constructor, the execution of this inside the function will point to fn as a new reference.

Modify the code as follows:

<script>
  let obj = {
    name: "小猪课堂",
    age: 20
  }
  // 手写bind函数
  Function.prototype.myBind = function (context) {
    const _this = this; // 当前函数
    let args = Array.from(arguments).slice(1); // 将参数列表转化为数组,除去第一个参数外
    // 返回新函数
    let fn = function () {
      // 如果被new调用,this应该是fn的实例
      return _this.apply(this instanceof fn ? this : (context || window), args.concat(Array.from(arguments)))
    }
    // 维护fn的原型
    let temp = function () { }
    temp.prototype = _this.prototype;
    fn.prototype = new temp(); // new的过程继承temp原型
    return fn
  };
  // 声明一个函数
  function fn(a, b, c) {
    console.log("函数内部this指向:", this);
    console.log("参数列表:", a, b, c);
  }
  let newFn = fn.myBind(obj, 10, 20);
  let newFn1 = fn.bind(obj, 10, 20)


  new newFn("myBind构造函数");
  new newFn1("bind构造函数");
</script>

Output result:

Is the output result of the above code the same as the output result of the actual bind function? To understand the above code, it is necessary for everyone to learn what happens to the new object in the following JS, mainly the following steps:

  • Create a new object
  • Assign this value of the constructor to the new object
  • Execute the constructor code and add properties to this new object
  • Return new object

The specific implementation process of new still needs to be understood by everyone.

Summarize

If you want to implement the bind function, you must understand its principles, which is nothing more than changing the point of this. The only difficulty is how to implement the execution of the constructor, that is, to understand what happens when a new object is created in js?

Guess you like

Origin blog.csdn.net/weixin_40918145/article/details/132832740
Recommended