【build your own xxx】实现你自己的bind函数

简介: 【build your own xxx】实现你自己的bind函数

今天来实现JavaScript的bind函数。 首先看MDN的bind函数描述:

image.png

image.png

从上面可以看出来,var A = B.bind(this)函数其实干了这几件事情:

  1. 返回一个函数,且这个函数后面运行时的this就是bind(this)传入的this
  2. 接收参数,这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面
  3. 使用new操作bind函数返回的函数时,之前传入的this会被忽略,也就是说new的优先级高于bind


第一步


首先实现第一步:

Function.prototype.Zbind = function (othis) {
        var originFunc = this;
    return function () {
      originFunc.apply(othis);
    }
  }
  var obj = {
  }
  function createAgumon() {
    this.name = "agumon";
    }
  var createAgumonBind = createAgumon.Zbind(obj);
  createAgumonBind();   
    obj;// {name: "agumon"}
复制代码


第二步


第二步考虑传参的问题,首先看看原生的bind函数是如何传参的:

var obj = {
    }
    function createAgumon(gender, age) {
        this.name = "agumon";
    this.gender = gender;
    this.age = age;
    }
    var createAgumonBind = createAgumon.bind(obj, 'female');
  createAgumonBind(22);
复制代码

可以看出来在bind函数中能先传部分参数,运行bind返回的函数时可以再传入部分参数。 自己实现:

Function.prototype.Zbind = function (othis) {
        var originFunc = this;
    var partArgs = [].slice.call(arguments, 1);
      var func = function() {};
    var boundFunc = function () {
      var finalArgs = partArgs.concat([].slice.call(arguments));
      return originFunc.apply(othis, finalArgs);
        }
        return boundFunc;
    }
    var obj = {
    }
    function createAgumon(gender, age) {
    this.name = "agumon";
    this.gender = gender;
    this.age = age;
    }
  var createAgumonBind = createAgumon.Zbind(obj, 'female');
  createAgumonBind(22);
    obj;// {name: "agumon", gender: "female", age: 22}
复制代码


第三步


使用new来调用bind返回的函数时,会忽略bind传入的thisnew操作和普通的函数调用有哪些区别?粗略的来讲,例如new F()这样的调用,有以下几个步骤:

1. 新建一个对象,var o = new Object()

1. 设置原型链,o.proto = F.prototype

1. 把F函数体内的this绑定为o,并且执行F函数的代码

1. 判断F的返回类型: 如果是值类型,则返回o 如果是引用类型,则返回该引用类型对象

开始实现:

Function.prototype.Zbind = function (othis) {
        var originFunc = this;
    var partArgs = [].slice.call(arguments, 1);
    var func = function() {};
    var boundFunc = function () {
      var finalArgs = partArgs.concat([].slice.call(arguments));
            return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
        }
      return boundFunc;
    }
    var obj = {}
    function createAgumon(gender, age) {
      this.name = "agumon";
      this.gender = gender;
      this.age = age;
  }
    var createAgumonBind = createAgumon.Zbind(obj, 'female');
    var newObj = new createAgumonBind(22);
    obj // {}
    newObj // {name: "agumon", gender: "female", age: 22}
复制代码

关键的地方在于这里:this instanceof boundFunc ? this : othis,如果是new操作的话,此时this的__proto__已经指向了boundFunc,所以使用instanceof可以检测出是否在使用new操作


小细节


原型丢失


刚刚实现的Zbind方法有个小问题:

Function.prototype.Zbind = function (othis) {
        var originFunc = this;
        var partArgs = [].slice.call(arguments, 1);
        var func = function() {};
        var boundFunc = function () {
          var finalArgs = partArgs.concat([].slice.call(arguments));
          return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
        }
        return boundFunc;
      }
      var obj = {
      }
      function createAgumon(gender, age) {
        this.name = "agumon";
        this.gender = gender;
        this.age = age;
      }
      createAgumon.prototype.college = 'THU'
      var createAgumonBind = createAgumon.Zbind(obj, 'female');
      var newObj = new createAgumonBind(22);
      console.log(newObj.college)// undefined
复制代码

可以看出来原型链丢失了,newObj.college得是'THU'才行

修改:

Function.prototype.Zbind = function (othis) {
        var originFunc = this;
        var partArgs = [].slice.call(arguments, 1);
        var func = function() {};
        var boundFunc = function () {
          var finalArgs = partArgs.concat([].slice.call(arguments));
          return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
        }
        func.prototype = originFunc.prototype;
        boundFunc.prototype = new func();
        return boundFunc;
      }
      var obj = {
      }
      function createAgumon(gender, age) {
        this.name = "agumon";
        this.gender = gender;
        this.age = age;
      }
      createAgumon.prototype.college = 'THU'
      var createAgumonBind = createAgumon.Zbind(obj, 'female');
      var newObj = new createAgumonBind(22);
      console.log(newObj.college)// 'THU'
复制代码

为什么要使用func.prototype = originFunc.prototype;boundFunc.prototype = new func();,而不是直接用**boundFunc.prototype = originFunc.prototype;**是因为这样写的话,修改boundFunc.prototype会影响到原函数的prototype。

that'all


参考资料:

mdn-bind

javascript中,new操作符的工作原理是什么?




相关文章
CMake Error: The source “xxx“ does not match the source “yyy“ used to generate cache. Re-run cmake
CMake Error: The source “xxx“ does not match the source “yyy“ used to generate cache. Re-run cmake
708 0
|
19天前
|
前端开发 API
【API管理 APIM】APIM中如何配置使用URL路径的方式传递参数(如由test.htm?name=xxx 变为test\xxx)
【API管理 APIM】APIM中如何配置使用URL路径的方式传递参数(如由test.htm?name=xxx 变为test\xxx)
|
2月前
|
JavaScript
js【详解】call()、apply()、bind()方法
js【详解】call()、apply()、bind()方法
31 6
|
2月前
|
Java Maven
@Date不管用怎么办,想少写get和setter方法,reate方法创建不了怎么办,Cannot resolve method ‘setxxx‘ in ‘xxx‘不管用怎么办,到Maven创建插件
@Date不管用怎么办,想少写get和setter方法,reate方法创建不了怎么办,Cannot resolve method ‘setxxx‘ in ‘xxx‘不管用怎么办,到Maven创建插件
解决 ERROR: cannot launch node of type [xxx]: can‘t locate node [xxx] in package [xxx]
解决 ERROR: cannot launch node of type [xxx]: can‘t locate node [xxx] in package [xxx]
638 0
|
JavaScript
js之call() apply() bind() $proxy()的总结
由以上代码可以总结出: 1. foo.call()与foo.apply()原理是一样的,将foo函数的this指向指定的对象(或者表述为在指定对象的上下文环境中运行foo函数); 2. bind(obj)与$proxy(obj)不同的是他们返回的是一个新的函数,该函数的this执行指定的对象obj。
135 0
js之call() apply() bind() $proxy()的总结
|
JavaScript 前端开发
【build your own xxx】实现你自己的call和apply
【build your own xxx】实现你自己的call和apply
【build your own xxx】实现你自己的call和apply