zoukankan      html  css  js  c++  java
  • 原生JS实现bind()函数

    一、bind()函数的两个特性:

    1、bind和curring,函数科里化

    function add(a, b, c) {
        var i = a+b+c;
        console.log(i);
        return i;
    }
    
    var func = add.bind(undefined, 100);//给add()传了第一个参数a
    func(1, 2);//103,继续传入b和c
    
    var func2 = func.bind(undefined, 200);//给func2传入第一个参数,也就是b,此前func已有参数a=100
    func2(10);//310,继续传入c,100+200+10

      可以利用此种特性方便代码重用,如下,可以不同的页面中只需要配置某几项,前面几项固定的配置可以选择用bind函数先绑定好,讲一个复杂的函数拆分成简单的子函数。

    2、bind和new

    function foo() {
        this.b = 100;
        console.log(this.a);
        return this.a;
    }
    
    var func =  foo.bind({a:1});
    func();//1
    new func();//undefined   {b:100},可以看到此时上面的bind并不起作用

      函数中的return除非返回的是个对象,否则通过new返回的是个this,指向一个空对象,空对象原型指向foo.prototype,空对象的b属性是100。也就是说通过new的方式创建一个对象,bind()函数在this层面上并不起作用,但是需要注意在参数层面上仍起作用,如下:

    function foo(c) {
        this.b = 100;
        console.log(this.a);
        console.log(c);
        return this.a;
    }
    
    var func =  foo.bind({a:1},20);
    new func();//undefined 20,通过new创建对象func,bind绑定的c依旧起作用

    二、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 () {
              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;
      };
    }

      第3行:传入oThis就是foo.bind({a:1})中传入的对象{a:1};

      第4行:判断调用此bind方法的对象是不是一个函数function,若不是则报错;

      第10行:

      (1)因为函数自带的arguments属性并不是一个数组,只是一个类数组,不具有slice这些方法,所以用call方法给slice()指定this为arguments,让arguments也可以实现slice()方法。
      (2)后面传入参数1,是slice(start, end)中的一个参数start,表示从arguments的小标为1,即第二个参数开始切割。 这里是将bind函数的参数数组取出来,第一个参数不要(就是不要oThis)也就是要被绑定方法的那个对象

      (3)arguments参数只有在函数调用执行的时候才存在,也就是当var func = foo.bind({a:1});的时候,调用了bind,此时aArgs是一个空数组。如果是var func = foo.bind({a:1}, 2),那么aArgs = [2];

      第12行,13行,20行,21行:创建了一个空对象FNOP,并将这个空对象的原型指向foo的原型;

      然后又将func/fBound的原型指向一个新的FNOP实例,这个步骤完成了给func/fBound拷贝一个FNOP的prototype即this/foo的prototype。

      其实这几句就相当于fBound.prototype = Object.create(this.prototype);

      第14行:

      (1)给 fBound/func return一个fToBind/foo对象;

      (2)这里的this指的是调用func()时的执行环境;直接调用func()的时候,this指向的是全局对象,那么结果是oThis/{a:1},这样就可以让这个fToBind的this指向这个传进来的对象oThis;

      (3)bind()同时也会传参数:aArgs.concat(Array.prototype.slice.call(arguments))

      【注意】这里的arguments是调用此函数时的arguments,也就是func()的执行环境,和上面的arguments(bind的执行环境)不一样,在此例中,此时的arguments是空数组,因为并没有给func()传参数。这段contact的意思就是把bind()中传的参数和func()中传的参数连起来,来实现上面提到的bind的科里性。

      (4)如果通过new func()来调用,this会指向一个空对象,这个空对象的原型会指向构造器的prototype的属性,也就是func/fBound的prototype属性。此时this instanceof fNOP 为true,那么返回的是this就是当前正常的this;相当于忽略掉bind的this的影响,实现了上述的bind特性二:bind和new。

      那么想想为什么要给func/fBound拷贝一个FNOP的prototype即this/foo的prototype?没有实现这个会怎样?

      我们知道bind()函数其实是实现了this的指定和参数的传递; 实际中的new func()其实相当于创建了func()一个新实例,使用的构造函数是func,它只为新对象定义了【默认的】属性和方法。也就是Object.create(this.prototype)的作用,如果不把foo的prototype拷贝个func,那么这里的new func()就没法得到foo默认的属性。 如图我把那两行注释掉后,za没办法获取到this.b的值

  • 相关阅读:
    比较字符串
    angular 兼容ie7 bootstrap2兼容ie6
    angular $watch
    CSS超过指定的宽度加省略号
    出去html中的标签
    andriod自定义视图
    andriod创建用户界面(1)
    安卓Activity生命周期(转)
    迷你版mvc框架执行过程
    【03月04日】A股滚动市盈率PE历史新低排名
  • 原文地址:https://www.cnblogs.com/goloving/p/9380076.html
Copyright © 2011-2022 走看看