ES5中bind()函数要点

ES5中bind函数的特性: 权威参考MDN

语法:fun.bind(thisArg[,arg1[,arg2[,...]]])
参数:
thisArg:当绑定函数被调用时,该参数会作为原函数运行时的this指向。当使用new操作符调用时绑定函数时,该参数无效。
arg1,arg2,...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:返回由指定的this值和初始化改造的原函数拷贝


  1. 不同于call和apply只是单纯的设置this的值后传参,它还会将所有bind()方法中的实参(第一个参数之后的参数)与this一起绑定。
    eg1:

    var sum = function(x,y){
        return x+y;
    }
    var succ = sum.bind(null,1) //让this指向null,其后的实参也会作为实参传入被绑定的函数sum
    succ(2); //输出3,可以看到1绑定了sum函数中的x
    

    eg2:

    var copy = function(){
        var args = Array.prototype.slice.call(arguments);
        console.log(args.join());
    }
    copy("hi","friends")   //输出   hi,friends
    var after = copy.bind(null,"hi","welcome");
    after("dear","friend");   //输出 hi,welcome,dear,friends
    
  2. bind()方法所返回的函数的length(形参数量)等于原函数的形参量减去传入bind()方法中的实参数量(除去第一个表示左右域的参数),因为传入到bind中的实参都会绑定到原函数的形参
    eg:

    function func(a,b,c,d,e){...}  //func.length = 5
    var after = func.bind(null,1,2); //这里传入两个实参绑定到了func函数的a,b
    console.log(after.length)  //输出为3
    
    注意:这里bind()方法的第一个参数为null,是因为func()函数内部没有任何与上下文相关的代码,所以不需要传递上下文对象
  3. 当bind()所返回的函数用作构造函数时,传入bind()的this将被忽略,实参会全部传入原函数
    eg:

   var person=function(y){
       this.name="Amy",
       this.printName=function(){console.log(this.name+" "+y)}
   }
   var anotherPerson={
       name:"Bob"
   }
   var Test = person.bind(anotherPerson,"Jack");
   var test = new Test();  //或者var test = new(Test);
   console.log(test.name)  //输出 "Amy"
   console.log(test.printName())  // 输出"Amy Jack"
   ```
   可以看到,Test为一个绑定函数,传入了anotherPerson作为上下文对象,传入了“Jack”作为预设参数,使用new调用Test时,传入的this完全被忽略,但是预设的实参能够被传入  

4. bind()方法返回的是一个带有固定作用域的新函数,以后不管在哪儿调用这个新函数,都不用担心作用域的问题,**即使是之后在使用call函数作用于这个新函数,也不会改变函数执行的上下文对象**  
   eg:
   ```
   var name = "Global";
   var student = {
       name:"John"
   }
   var person = {
       name:"Amy",
       sayName:function(){console.log(this.name)}
   }
   var one={
       name:"Bob"
   }
   var sayName = person.sayName;
   var bindSayName = person.sayName.bind(student);
   console.log(person.sayName())  // "Amy"
   console.log(sayName())       // "Global"
   console.log(bindSayName())  //"John"
   console.log(bindSayName.call(one))  //"John"
   ```
   此时bindSayName这个函数已经绑定到了student这个上下文,接下来用call调用它也不会改变上下文对象

5. 配合setTimeout和setInterval函数使用
一般在函数或类的实例中使用setTimeout和setInterval函数时,this会默认指向window对象,所以这时需要显示的绑定this对象  
   eg:
   ```
   var name = "Global";
   var person = {
       name:"Amy",
       sayName:function(){console.log(this.name)},
       printName:function(){
           setTimeout(this.sayName,1000);
       }
   }
   console.log(person.printName())   //"Global"
   ```
   出现上述问题的原因是setTimeout把person的printName函数放到全局函数作用域中执行去了,所以打印的是全局作用域中的name变量,故而需要手动的在setTimeout函数中绑定this对象,改进如下:
   ```
   var name = "Global";
   var person = {
       name:"Amy",
       sayName:function(){console.log(this.name)},
       printName:function(){
           setTimeout(this.sayName.bind(this),1000);
       }
   }
   console.log(person.printName())  //"Amy"
   ```
6. 兼容性问题:
bind()是在ES5中加入的新特性,所以无法在所用浏览器上运行,例如IE6、7、8都不支持,因此需要手动的实现bind()函数  
   ##### 贴出polyfill官方文档上的实现:
   ```
   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), //(1)
           fToBind = this,  //(3)
           fNOP = function () {},
           fBound = function () {
             return fToBind.apply(this instanceof fNOP? this: oThis ||this,
                   aArgs.concat(Array.prototype.slice.call(arguments))); //(2)
           };
   
       fNOP.prototype = this.prototype;  //(4)
       fBound.prototype = new fNOP(); //(5)
   
       return fBound;
     };
   }
   ```
   理解:
   **以1中的eg2举个例子:**
   ```
   var copy = function(){
       var args = Array.prototype.slice.call(arguments);
       console.log(args.join());
   }
   copy("hi","friends")   //输出   hi,friends
   var after = copy.bind(null,"hi","welcome");
   after("dear","friend");   //输出 hi,welcome,dear,friends
   ```
   (1) `aArgs`是定义bind函数时传入的除第一个参数oThis之外的预设参数,在eg2中为["hi","welcome"]  
   (2) 这里的`arguments`是bind绑定后返回的函数`after`调用时传入的参数,其是类数组对象,调用Array.prototype.slice.call(arguments)后转化为数组,在eg2中为["dear","friends"]   
   (3) 这里的fToBind就是被绑定的原来的函数"copy"  
   (4) 本来`fBound.prototype = fToBind.prototype`就可以让新函数`after`继承原来函数`copy`的所有属性,但这样新函数和原来被绑定的函数指向同一个地方,对新函数的任何修改会影响到原来被绑定的那个函数,不符合要求,所以引入fNOP这个函数作为中转,让bind绑定后返回的函数fBound指向继承了被绑定的函数fToBind的所有属性的一个新的函数fNOP,此时之后对fBound函数做的修改只会影响fNOP这个函数,而与fToBind没有任何关系

你可能感兴趣的:(ES5中bind()函数要点)