Day33:this的绑定规则
【书名】:你不知道的JavaScript(上卷)
【作者】:Kyle Simpson
【本书总页码】:213
【已读页码】:106
1. 默认绑定(独立函数调用时)
非严格模式下,默认绑定到全局对象;严格模式下,全局对象将无法使用默认绑定,因此 this 会绑定到 undefined。
2. 隐式绑定
调用位置是否有上下文对象。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2,this指向obj
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42 this指向的是obj2
隐式绑定的特殊情况——隐式丢失
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
一种更微妙、更常见并且更出乎意料的情况发生在传入回调函数时:
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn 其实引用的是 foo
fn(); // <-- 调用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
doFoo( obj.foo ); // "oops, global"
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以this隐式绑定隐式丢失。
3. 显式绑定——call(..) 和 apply(..)
它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个 this。
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2 this显式绑定到obj
如果传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作 this 的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)、new Boolean(..)或者new Number(..))。这通常被称为“装箱”。
function foo () {
console.log(this.toString());
}
foo.apply('hello'); // hello
显式绑定依然无法解决绑定丢失问题。
3.1. 硬绑定解决绑定丢失
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的 this
bar.call( window ); // 2
硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值:
另一种使用方法是创建一个可以重复使用的辅助函数:
由于硬绑定是一种非常常用的模式,所以在 ES5 中提供了内置的方法 Function.prototype.bind,它的用法如下:
bind(..) 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数。
3.2. API调用的“上下文”
第三方库的许多函数,以及 JavaScript 语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为“上下文”(context),其作用和 bind(..) 一样,确保你的回调函数使用指定的 this。
这些函数实际上就是通过 call(..) 或者 apply(..) 实现了显式绑定。
4. new绑定
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行[[原型]]连接。
3. 这个新对象会绑定到函数调用的this。
4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
使用 new 来调用 Foo(..) 时,会构造一个新对象并把它绑定到 Foo(..) 调用中的 this上。