Skip to content
本页目录

浅谈“this”(下)

问题

在上篇博客中,我们简单说了调用栈、默认绑定、隐式与显式绑定等几种规则,还记得文章末留下的问题嘛?

我们通过硬绑定还修改 this 的绑定位置,但是这存在着一个问题,一旦使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this 的指向, 函数的灵活性大大降低,作为一个有追求的码农,有没有一种办法既可以自定义 this 的指向,又可以动态的修改呢?

答案是有的,我们可以通过一种被称之为软绑定(我也不知道这名字谁取的)的方式来实现,回想一下我们前面提到的默认绑定,在独立函数调用时他会将 this 默认绑定到全局对象(严格模式下为undefined)上,思考一下,如果我们可以给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力,即称之为软绑定

talk is cheap,上代码

js
if(!Function.prototype.softBind){
    Function.prototype.softBind = function(obj){
         var fn = this;
         //捕获所有 curried 参数
         var curried = [].slice.call( arguments ,1 )
         var bound = function(){
             return fn.apply(
                 (!this || this === (window || global))?
                    obj : this 
                curried.concat.apply( curried ,arguments )
             );
         };
         bound.prototype = Object.create(fn.prototype);
         return bound;
    };
}

上面的代码可能难得读懂,大致的意思就是:首先检查调用时的 this ,如果 this 绑定到全局对象或者undefined,那就把指定的默认对象绑定到this,否则不会修改this。 除了软绑定之外,softBind()的其他原理和ES5内置的bind()类似,他会对指定的函数进行封装, 下面我们来看看softBind是否实现了软绑定功能:

js
function foo(){
    console.log("name" + this.name);
}
var obj = {name : "wanglang"},
var obj2 = {name : "heiguo"},
var obj3 = {name : "weige"};

var fooOBJ = foo.softBind(obj);

fooOBJ();// name : wanglang

obj2.foo =foo.softBind(obj);
obj2.foo(); // name : heiguo  <-发现了吗 this被修改了

fooOBJ.call(obj3); // name : weige  

setTimeout(obj2.foo,10);
//name : wanglang  <----软绑定

从上面的代码我们可以看到,软绑定版本的foo()可以手动将this 绑定到obj2或者obj3上,但如果应用默认绑定,则会将this绑定到obj。

注意

上面我们说了关于this绑定的几种规则,但有一个特例,在ES6语法中的箭头函数是不适用于这些规则的,它的this指向是根据外层作用域来决定的。

举例说明:

js
function foo(){
    //返回一个箭头函数
    return (a) =>{
        console.log(this.a);//this继承自foo()
    }
}
var obj1 = {
    a: 1
};
var obj2 = {
    a:2
};
var bar = foo.call(obj1);
bar.call(obj2);//输出结果为1,而不是2

foo()内部创建的箭头函数会捕获调用时foo的 this,由于foo()的 this 绑定到obj1,bar(引用箭头函数)的 this 也会绑定到obj1,箭头函数的绑定是无法被修改的。

总结

通过这两篇博客,对 this 的指向或许有了一些新的认识,也让我巩固了一下前面学到的知识,果然,温故而知新。

Released under the MIT License.