bind函数源码解析

来源:互联网 发布:淘宝卖家哪个商品好用 编辑:程序博客网 时间:2024/05/22 15:39

bind函数

ES5中原生bind函数是用于绑定this指向的

   var obj = {      x: 1   }   function show(){       console.log(this.x);   }   var newShow = show.bind(obj);   newShow();    // 1

bind方法会创建一个新函数。当这个新函数被调用时,bind的第一个参数将作为它运行时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。
bind返回的绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数

初级实现

我们来实现一个初级的bind函数Polyfill:

   Function.prototype.bind = function(context){      //that为调用bind函数的对象      var that = this;      //bind函数可能入其他参数,先将参数类数组转化为数组      var argsArray = Array.prototype.slice.call(arguments);      return function(){          // slice只传一个参数,返回从参数开始到数组末尾的元素          // slice(1)之后为其他参数          return that.call(context, argsArray.slice(1))      }   }

译者注:如果对这个函数有所不理解,我们可以先看一下不改变this的改造

   function a(){ console.log(1) };   Function.prototype.bind = function(){      return this;   }   var b = a.bind();   b();   // 1

进行嗅探增加程序健壮性

其实是一个典型的“Monkey patching(猴子补丁)”,即“给内置对象扩展方法”。所以,如果能进行一下“嗅探”,进行兼容处理,就是锦上添花了。

   Function.prototype.bind = Function.prototype.bind || function(context){      //...   }

柯里化(curring)实现

我们返回的参数列表里包含:atgsArray.slice(1),他的问题在于存在函数的参数丢失的现象。

   Function.prototype.bind = Function.prototype.bind || function (context) {       var that = this;       //通过bind预先传入的参数       var args = Array.prototype.slice.call(arguments, 1);       return function () {           //函数调用时传入的参数            var innerArgs = Array.prototype.slice.call(arguments);            var finalArgs = args.concat(innerArgs);            return that.apply(context, finalArgs);        }   }

但是,我们注意在上边bind方法介绍的第三条提到:bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们的绑定this就需要“被忽略”

构造函数场景下的兼容

直接上代码

    Function.prototype.bind = Function.prototype.bind || function (context) {        var that = this;        var args = Array.prototype.slice.call(arguments, 1);        var F = function () {};        F.prototype = this.prototype;        var bound = function () {            var innerArgs = Array.prototype.slice.call(arguments);            var finalArgs = args.concat(innerArgs);            return that.apply(this instanceof F ? this : context || this, finalArgs);        }        //把新函数的原型清空        bound.prototype = new F();        return bound;    }

更严谨的做法

我们需要调用bind方法的一定要是一个函数,所以可以在函数体内做一个判断:

    if (typeof this !== "function") {      //使用类型错误      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");    }

一切还没完

es5-shim源码如下所示

   bind: function bind(that) {        var target = this;        if (!isCallable(target)) {            throw new TypeError('Function.prototype.bind called on incompatible ' + target);        }        var args = array_slice.call(arguments, 1);        var bound;        var binder = function () {            if (this instanceof bound) {                var result = target.apply(                    this,                    array_concat.call(args, array_slice.call(arguments))                );                if ($Object(result) === result) {                    return result;                }                return this;            } else {                return target.apply(                    that,                    array_concat.call(args, array_slice.call(arguments))                );            }        };        var boundLength = max(0, target.length - args.length);        var boundArgs = [];        for (var i = 0; i < boundLength; i++) {            array_push.call(boundArgs, '$' + i);        }        bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);        if (target.prototype) {            Empty.prototype = target.prototype;            bound.prototype = new Empty();            Empty.prototype = null;        }        return bound;    }
0 0
原创粉丝点击