导语:
本文主要归纳总结自《你不知道的JavaScript上卷》中的关于this的解析。
- this既不指向函数自身也不止像函数的词法作用域;
- this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
- this的绑定和函数声明的位置没有任何关系,只取决于函数的调用。
this的四种绑定规则
1.默认绑定
function foo(){
console.log(this.a);
}
var a=2;
foo(); // 输出2,原因:foo在全局作用域里被调用,所以this绑定的是全局作用域。
function foo(){
"use strict"
console.log(this.a);
}
var a=2;
foo(); // TypeError:this is undefined.
若使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined。
function foo(){
console.log(this.a);
}
var a=2;
(function(){
"use strict"
foo(); //输出2。
})();
在严格模式下调用foo()则不影响默认绑定。
function foo(){
console.log(this.a);
}
var a=2;
(function(){
"use strict";
foo(); //输出2。
})();
2.隐式绑定
考虑:调用位置是否有上下文对象,或者是否被某个对象拥有或包含。
1–>隐式绑定
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
}
obj.foo(); // 输出2 原因:foo()再被调用的时候,前面加上了对obj的引用,当函数引用有上下文对象时,函数中的this会绑定上去。
var obj1={
a:42;
obj:obj
}
obj1.obj.foo(); //输出2 原因:对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
2–>隐式丢失
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
}
var bar=obj.foo;
var a="oops,global";
bar(); //输出oops,global
原因:虽然bar是obj.foo的一个引用,但实际它引用的是foo函数本身,因此bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
3–>回调函数丢失this绑定
function foo(){
console.log(this.a);
}
function doFoo(fn){
//fn其实引用的是foo
fn();
}
var obj={
a:2,
foo:foo
};
var a="oops,global";
doFoo(obj.foo); //输出"oops,global" 相当于fn=obj.foo;fn();
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。
作为参数传入内置函数,也会发生隐式丢失
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
var a="oops,global";
setTimeout(obj.foo,100);////输出"oops,global"
3.显示绑定
使用call(..)和apply(..),他们的第一个参数是一个对象,在调用函数时将其绑定到this。
1–>显示绑定
function foo(){
console.log(this.a);
}
var obj={
a:2
}
foo.call(obj); //输出2
如果你传入一个原始值(字符串类型、布尔类型)来当做this的绑定对象,这个原始值会被转换成他的对象形式。称为“装箱”。
问题:显示绑定无法解决我们之前的丢失绑定问题?但显示绑定的变种——硬绑定可以解决。
2–>硬绑定
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
硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值;
3–>bind
由于硬绑定是一种非常常用的模式,所以ES5提供了内置的方法:
Function.prototype.bind:
function foo(something){
console.log(this.a,something);
return this.a+something;
}
var obj={
a:2
};
var b=bar(3);
console.log(b);
4.new绑定
function foo(a){
this.a=a;
}
var bar=new foo(2);
console.log(bar.a);//输出2
使用new来调用foo(…)时,我们会构造一个新对象并把它绑定到foo(…)调用中的this上。