手写实现bind

一: 为什么有bind

在ES6的箭头函数出现之前,我们在用JS编程的时候经常遇到this指向不明的坑。常见的情况有下面几种

1、

    obj = {
        attr:'attr',
        fun:function(){
             console.log(this.attr);
             }
    }
    fun2 = obj.fun; 
    obj.fun(); //attr
    fun2(); // undefined

2、

    obj = {
        attr:'attr',
        fun:function(){
             settimeout(function(){
                   console.log(this.attr);
                   },0)
             }
    }
    obj.fun(); // undefined

等等、其实不管什么情况,根本原因就是当这个函数运行时上下文和你定义函数时认为的执行上下文不一致导致的,bind天生就是解决这方面问题的。我们来看如何使用bind

    obj = {
        attr:'attr',
        fun:function(){
             settimeout(function(){
                   console.log(this.attr);
                   }.bind(this),0)
             }
    }
    obj.fun(); // attr

bind做了什么?从上面的列子中我们可以看出来大概了,先看bind的基础用法。fun.bind(obj [, a , b , c ])
bind方法可以接收参数,其中第一个参数obj 是绑定bind的调用者fun的执行上下文为obj.prototype,后面的参数是当函数调用的时候默认绑定给调用者fun的.我们不妨写一个简单的bind方法,再来简单分析。

二: 手写bind版本

if(Function.prototype.bind === undefined){
    console.log('您的浏览器不支持bind方法! 开始使用手写bind功能!');
    Function.prototype.bind = function(obj){
        var arg1 = [].slice.call(arguments,1);  // 解释一下骚操作,用arg1 保留了 当函数调用bind方法时候传  入的参数,因为arguments是类数组对象,我们借用了数组的slice方法
        var fun = this; // fun —> bind调用者(也就是某个函数)
        return function(){
            fun.apply(obj,arg1.concat([].slice.call(arguments,1)));
            //  好像又引入了apply这种骚东西,是的后面我们再分析。
            //  这里返回了一个闭包函数, 里面可以使用 obj , arg1 , fun 这些变量,配合起来实现了bind
            //  感兴趣的朋友可以用ie8测试 bind已经生效了  
        }
}
}else{
    console.log('您的浏览器支持bind方法!')
}

测试代码

obj = { name : 'Tom' };
function fun(){
    console.log(this.name);
}
fun.bind(obj)(); 

打开控制台会看到

您的浏览器不支持bind方法! 开始使用手写bind功能!
Tom

当然我们这种写法是可以实现bind的基础功能,但是并不健壮。我们拿MDN的标准源码来分析观摩一下

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 () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

如果看到这里还是一头雾水的兄弟,建议先去看看 apply call bind 三个函数的区别。如果连prototype也不懂的童鞋,就当我没说过这个233~~

你可能感兴趣的:(手写实现bind)