JS-this小解析(2)

绑定例外

被忽略的this

如果把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

function foo() {
    console.log(this.a );
}
var a = 2;
foo.call(null);// 2

如果需要传入null,则需要使用apply(..)来“展开”一个数组,并当做参数传入一个函数。bind(..)可以对参数进行柯里化(预先设置一些参数)。

更安全的this

总是使用null来忽略this绑定可能产生一些副作用。如果某个函数确实使用了this(比如第三方库中的一个函数),那默认绑定规则会把this绑定到全局对象(在浏览器中这个对象是window),这将导致不可预计的后果(比如修改全局对象)。

一种“更安全”的做法是传入一个特殊的对象,把this绑定到这个对象不会对你的程序产生任何副作用。由于这个对象完全是一个空对象,可以用变量名ø(这是数学中表示空集合符号的小写形式)来表示它,或者其他任何名字。

在JavaScript中创建一个空对象最简单的方法都是Object.create(null)。Object.create(null)和{}很像,但是并不会创建Object.prototype这个委托,所以它比 {}“更空”。

间接引用

有时候可能(有意或者无意地)创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。
间接引用最容易在赋值时发生:

function foo() {
    console.log(this.a );
}
var a = 2;
var o = {
    a:3,
};
var p = {
    a:4
};
o.foo();// 3
(p.foo =o.foo)();// 2

赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo(),所以这里会应用默认绑定。注意:对于默认绑定来说,决定this绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this会被绑定到undefined,否则this会被绑定到全局对象。

软绑定

之前我们已经看到过,硬绑定这种方式可以把this强制绑定到指定的对象(除了使用new时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改this。

如果可以给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改this的能力。

可以通过一种被称为软绑定的方法来实现我们想要的效果:

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;
    };
}

除了软绑定之外,softBind(..)的其他原理和ES5内置的bind(..)类似。它会对指定的函数进行封装,首先检查调用时的this,如果this绑定到全局对象或者undefined,那就把指定的默认对象obj绑定到this,否则不会修改this。此外,这段代码还支持可选的柯里化(详情请查看之前和bind(..)相关的介绍)。

下面我们看看softBind是否实现了软绑定功能:

function  foo() {
    console.log("name: "+this.name);
}
var obj = {name:"obj"},
    obj2 = {name:"obj2"},
    obj3 = {name:"obj3"};
var fooOBJ = foo.softBind(obj );
fooOBJ();// name: obj
obj2.foo =foo.softBind(obj);
obj2.foo();// name: obj2 <---- 看!
fooOBJ.call(obj3 );// name: obj3 <---- 看!
setTimeout(obj2.foo,10);// name: obj <---- 应用了软绑定

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

this词法

ES6中介绍了一种无法使用这些规则的特殊函数类型:箭头函数。(由“胖箭头”操作符 =>定义)箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

function foo(){
    // 返回一个箭头函数
    return(a)=>{
        //this继承自foo()        
        console.log(this.a );
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 );// 2, 不是3!

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

箭头函数最常用于回调函数中,例如事件处理器或者定时器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值