js中bind的实现

众所周知,bind、call、apply都是用来改变this指向的,其中bind是返回一个函数,你需要再次手动调用。

举例:

var obj = {
        a: 1,
        func: function() {
            console.log(this.a)
        }
    }

// 需要在最后加个括号手动执行func方法
obj.func.bind({a:2})()  // 2

实现方式很简单:

Function.prototype.bind = function(context){
  // 取出bind方法中传入的除第一个参数(第一个参数是需要绑定的this)外的其余参数存在args数组中
  var args = Array.prototype.slice.call(arguments, 1),
  // 这里的this是指调用bind方法的函数
  self = this;
  return function(){
      // 获取执行bind函数传入的参数
      var innerArgs = Array.prototype.slice.call(arguments);
      // 将第二个括号中的参数concat进args得到除第一个参数外所有传入的参数(这里有两个知识点:1、因为闭包args参数的值一直存在在内存中;2、偏函数(和函数柯里化相似但有点不同))
      var finalArgs = args.concat(innerArgs);
      // 调用apply方法,return函数结果
      return self.apply(context,finalArgs);
  };
};

想必上面的实现代码大家都能看懂,我们再看一个构造函数调用bind后执行的结果:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() { 
  return this.x + ',' + this.y; 
};

var YAxisPoint = Point.bind(null, 0);   // 第1行
var axisPoint = new YAxisPoint(5);  // 第2行
axisPoint.toString(); // '0,5'   第3行

axisPoint instanceof Point; // true    第4行
axisPoint instanceof YAxisPoint; // true    第5行
new Point(17, 42) instanceof YAxisPoint; // true    第6行

其中,第5行代码不难理解,因为axisPoint是YAxisPoint new出来的对象,理所当然是YAxisPoint的实例。

但是第4行axisPoint也是Point的实例,那就说明YAxisPoint的原型和Point的原型是继承关系或者说他们的原型指向同一个原型。

再看第6行,Point的实例指向YAxisPoint(即第4行和第6行有点相互牵制的意思),所以说明YAxisPoint的原型和Point的原型指向同一个原型,因为如果是继承关系的话,第4行和第6行总有一个会是false。

最终实现:

Function.prototype.bind = function (context) {
        // 如果调用bind的不是一个function类型,直接报错,不再向下执行
        if (typeof this !== "function") {
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var args = Array.prototype.slice.call(arguments, 1),
        self = this,
        bound = function () {
            // 这里和上面一版实现不同的是,apply的this指向做了一点改动,如果是构造函数调用,那么apply传入的第一个参数会被忽略,其余参数仍然可用(这里为什么这么写,其实没有太明白)
            return self.apply(
                this instanceof self ? this : context || window,
                args.concat(Array.prototype.slice.call(arguments))
            );
        };
        // 针对bind调用的函数是构造函数的场景,通过上面分析,调用bind的构造函数的原型和bind返回的函数的原型指向同一个原型,即将this.prototype赋值给bound.prototype
        bound.prototype = this.prototype;
        return bound;
    }; 

参考文章:

https://www.cnblogs.com/heshan1992/p/6667596.html

偏函数与柯里化区别:

  柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。

  偏函数则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。

       n元就是n个参数

 

文章如有理解错误之处,不吝赐教~

posted @ 2021-11-26 17:11  redRunZhy  阅读(1273)  评论(0编辑  收藏  举报