一:被忽略的this
除了前面4种绑定规则之外,还有一些绑定行为是出乎我们的意料的,比如给call、apply、bind绑定一个null或者undefined,那么这时候就不是应用显示绑定了,而是会变成默认绑定。
1:null和undefined作为this的占位值
function foo(a, b) {
console.log("a: " + a, "b: " + b)
}
foo.apply(undefined, [1, 2, 3]);
如果不关心this的指向,可以使用这种方式,传入一个null或者undefined作为一个占位值。但是这样会把this绑定到全局作用域,如果在复杂的程序中使用,可能会导致一些比较难发现的bug。
2:"更安全"的this
相比于传入null和undefined,传入一个空对象会更加地安全。创建一个空对象的方法如下:
let obj = Object.create(null);
console.log(obj)
这种方式创建的空对象是没有prototype的,完全是空的。
现在将创建的空对象传入apply中
let obj = Object.create(null);
function foo(a, b) {
console.log("a: " + a, "b: " + b)
}
foo.apply(obj, [1, 2])
使用变量名可以让函数更加“安全”,同时可以提高代码的可读性。
二:间接引用
间接引用比较多的情况是发生在赋值的时候,如:
function foo() {
console.log(this.a)
}
var a = 2;
var o = {
a: 3,
foo: foo
}
var p = {
a: 4
}
o.foo(); // 3
(p.foo = o.foo)(); // 2
console.log((p.foo = o.foo)) // 返回foo函数的引用
表达式:(p.foo = o.foo) 返回的是foo函数的引用,因此 (p.foo = o.foo)() 就是相当于在全局作用域调用(也就是发生了默认绑定),所以就会返回2。对于默认绑定来说,决定this的绑定对象是 是否处在严格模式下,而不是函数的调用位置。如果处于严格模式下,this被绑定到undefined;否则是绑定到全局对象window。
三:软绑定
if(!Function.prototype.softBind) {
Function.prototype.softBind = function() {
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;
}
}
function foo() {
console.log("name: " + this.name)
}
var obj = {name: 'obj'};
var obj2 = {name: 'obj2'};
var 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
软绑定可以提升函数的灵活性,前面说的硬绑定,使用了之后就无法使用影视绑定或者显示绑定来修改this
这边定义了一个softBind()方法,以及它的使用例子。
本文内容学习于《你不知道的JavaScript》上卷