手动实现ES5的bind方法

bind功能特性

  1. bind方法创建一个新函数,改变函数的作用域,并且不同于call、apply,函数不执行
  2. bind可用于实现偏函数,则实现时需要同时保存bind()被传入的参数arguments以及返回的新创建函数被传入的参数arguments
function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

    3.bind用于构造函数时候this作用域改变无效,核心点是在继承原函数的原形__proto__的时候增加一个fNOP父类,即 原型链继承方式 来判断原函数是否是new的构造函数,只有构造函数返回的对象才可以继承原型链

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

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

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// 以下这行代码在 polyfill 不支持,
// 在原生的bind方法运行没问题:
//(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持)
var YAxisPoint = Point.bind(null, 0/*x*/);

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true

原理

  1. 使用Array.prototype.slice.call()方法获取bind函数的arguments
  2. 使用Array.prototype.concat方法连接bind以及新函数的arguments以获得所有的arguments
  3. 使用原型链方法判断原this是否是new构造函数所得到的对象

代码实现

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {//如果原函数不是function类型,抛出类型错误
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),//获得bind方法的参数
        fToBind = this,         //保存调用函数
        fNOP    = function() {},//用来充当fBound的父类
        fBound  = function() {  //即将返回的新创建的函数
          // this instanceof fNOP === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}

 

你可能感兴趣的:(手动实现ES5的bind方法)