关于JavaScript中this的软绑定

首先,什么是软绑定?

所谓软绑定,是和硬绑定相对应的一个词,在详细解释软绑定以前,咱们先来看看硬绑定。在JavaScript中,this的绑定是动态的,在函数被调用的时候绑定,它指向什么彻底取决于函数在哪里调用,状况比较复杂,光是绑定规则就有默认绑定、隐式绑定、显式绑定、new绑定等,而硬绑定是显式绑定中的一种,一般状况下是经过调用函数的  apply() 、  call() 或者ES5里提供的  bind() 方法来实现硬绑定的。

硬绑定有什么问题,为何须要软绑定

上述三个方法好是好,能够按照本身的想法将函数的this强制绑定到指定的对象上(除了使用new绑定能够改变硬绑定外),可是硬绑定存在一个问题,就是会下降函数的灵活性,而且在硬绑定以后没法再使用隐式绑定或者显式绑定来修改this的指向。java

在这种状况下,被称为软绑定的实现就出现了,也就是说,经过软绑定,咱们但愿this在默认状况下再也不指向全局对象(非严格模式)或  undefined (严格模式),而是指向二者以外的一个对象(这点和硬绑定的效果相同),可是同时又保留了隐式绑定和显式绑定在以后能够修改this指向的能力。

软绑定的具体实现

在这里,我用的是《你不知道的JavaScript 上》中的软绑定的代码实现:闭包

 1 if(!Function.prototype.softBind){
 2     Function.prototype.softBind=function(obj){
 3         var fn=this;
 4         var args=Array.prototype.slice.call(arguments,1);
 5         var bound=function(){
 6             return fn.apply(
 7                 (!this||this===(window||global))?obj:this,
 8                 args.concat.apply(args,arguments)
 9             );
10         };
11         bound.prototype=Object.create(fn.prototype);
12         return bound;
13     };
14 }

咱们先来看一下效果,以后再讨论它的实现。app

 1 function foo(){
 2     console.log("name: "+this.name);
 3 }
 4 
 5 var obj1={name:"obj1"},
 6     obj2={name:"obj2"},
 7     obj3={name:"obj3"};
 8 
 9 var fooOBJ=foo.softBind(obj1);
10 fooOBJ();//"name: obj1" 在这里软绑定生效了,成功修改了this的指向,将this绑定到了obj1上
11 
12 obj2.foo=foo.softBind(obj1);
13 obj2.foo();//"name: obj2" 在这里软绑定的this指向成功被隐式绑定修改了,绑定到了obj2上
14 
15 fooOBJ.call(obj3);//"name: obj3" 在这里软绑定的this指向成功被硬绑定修改了,绑定到了obj3上
16 
17 setTimeout(obj2.foo,1000);//"name: obj1"
18 /*回调函数至关于一个隐式的传参,若是没有软绑定的话,这里将会应用默认绑定将this绑定到全局环
19 境上,但有软绑定,这里this仍是指向obj1*/

能够看到软绑定生效了。下面咱们来具体看一下 softBind() 的实现。函数

 

在第一行,先经过判断,若是函数的原型上没有  softBind() 这个方法,则添加它,而后经过  Array.prototype.slice.call(arguments,1) 获取传入的外部参数,这里这样作其实为了函数柯里化,也就是说,容许在软绑定的时候,事先设置好一些参数,在调用函数的时候再传入另外一些参数(关于函数柯里化你们能够去网上搜一下详细的讲解)最后返回一个  bound 函数造成一个闭包,这时候,在函数调用  softBind() 以后,获得的就是  bound 函数,例如上面的  var fooOBJ=foo.softBind(obj1) 。
 
在  bound 函数中,首先会判断调用软绑定以后的函数(如fooOBJ)的调用位置,或者说它的this的指向,若是  !this (this指向undefined)或者  this===(window||global) (this指向全局对象),那么就将函数的this绑定到传入  softBind 中的参数obj上。若是此时this不指向undefind或者全局对象,那么就将this绑定到如今正在指向的函数(即隐式绑定或显式绑定)。  fn.apply 的第二个参数则是运行  foo 所须要的参数,由上面的args(外部参数)和内部的arguments(内部参数)链接成,也就是上面说的柯里化。
 
其实在第一遍看这个函数时,也有点迷,有一些疑问,好比  var fn=this 这句,在  foo 经过  foo.softBind() 调用  softBind 的时候,fn到底指向谁呢?是指向foo仍是指向softBind?咱们能够写个demo测试,而后能够很清晰地看出fn指向什么:
 1 var a=2;
 2 function foo(){
 3 }
 4 foo.a=3;
 5 Function.prototype.softBind=function(){
 6     var fn=this;
 7     return function(){
 8         console.log(fn.a);
 9     }
10 };
11 Function.prototype.a=4;
12 Function.prototype.softBind.a=5;
13 
14 foo.softBind()();//3
15 Function.prototype.softBind()();//4
能够看出,fn(或者说this)的指向仍是遵循this的绑定规则的,  softBind 函数定义在Function的原型  Function.prototype 中,可是JavaScript中函数永远不会“属于”某个对象(不像其余语言如java中类里面定义的方法那样),只是对象内部引用了这个函数,因此在经过下面两种方式调用时,fn(或者说this)分别隐式绑定到了  foo 和  Function.prototype ,因此分别输出3和4。后面的  fn.apply() 也就至关于  foo.apply() 。
 
注:这篇文章我首载于掘金,因为一些我的缘由如今才发到博客园,原文可见于 http://www.javashuo.com/article/p-ahgsjgas-ez.html
相关文章
相关标签/搜索