简单实现js中的bind

bind官方定义:bind()方法创建一个新的函数, 当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列

apply官方定义:apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数

var obj = { x: 1 };
function add(y) {
  alert(this.x);
  alert(arguments);
}
add.apply(obj, [1, 2]);
var addbind = add.bind(obj, 1, 2);
addbind(3);

上面这段代码二者最终效果是等价的,区别就在于bind返回的是一个函数

按照bind的定义,我们要返回一个函数,这个函数只要调用add.apply(obj, [1, 2]);就能达到我们的效果

Function.prototype.testBind = function() {
  var fBound  = function() {
    return add.apply(obj, [1, 2]);
  };
  return fBound;
}

bind方法第一个入参是this值,我们修改函数传入this,也就是apply中的obj

Function.prototype.testBind = function(oThis) {
  var fBound  = function() {
    return add.apply(oThis, [1, 2]); //obj替换为oThis
  };
  return fBound;
}

那么下一步我们要解决add替换的问题,在js中this永远指向最后调用它的对象add.bind(obj, 1, 2);,那么bind的调用对象就是add,所以bind函数中的this也就是add这个调用对象

Function.prototype.testBind = function(oThis) {
  var fToBind = this; //bind的调用对象add
  var fBound  = function() {
    return fToBind.apply(oThis, [1, 2]); // add替换为fToBind
  };
  return fBound;
}

接下来解决arguments问题

Function.prototype.testBind = function(oThis) {
  var fToBind = this; //bind的调用对象add
  var aArgs   = Array.prototype.slice.call(arguments, 1);
  var fBound  = function() {
    var args = [...aArgs,...arguments]; // 拼接add函数参数
    return fToBind.apply(oThis, args); 
  };
  return fBound;
}

到此,一个简单的bind方法就实现了,但是bind方法远没有这么简单,还要处理原型链以及被用作构造函数的场景,官方实现

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        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;
  };
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。