1.默認綁定
function foo( ){
console.log(this.a);
}
var a = 2;
foo( ); // 2
在代碼中,foo()是直接使用不帶任何修飾的函數引用進行調用的,因此只能使用默認綁定。默認綁定this指向全局變量。
如果處于嚴格模式(strict mode),那么全局變量將無法使用默認綁定,因此this會綁定到undifined:
function foo( ){
"use strict";
console.log(this.a);
}
var a = 2;
foo( ); //undefined
雖然this的綁定規(guī)則完全取決于調用位置,但是只要foo()運行在非 strict mode下時,默認綁定才能綁定到全局變量;嚴格模式下與foo( )的調用位置無關:
function foo( ){
console.log(this.a);
}
var a = 2;
(function(){
"use strict";
foo(); //2
})();
2.隱式綁定
function foo( ){
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); //2
foo函數嚴格來說并不屬于obj對象。然而,調用位置會使用obj的上下文來引用對象。當函數擁有上下文對象時,隱式綁定規(guī)則會把函數中的this綁定到這個上下文對象。this.a 和 obj.a是一樣的。
對象引用鏈中只有最后一層會影響調用位置。
function foo( ){
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2;
};
obj1.obj2.foo(); // 42
隱式丟失(取決于是否是嚴格模式):
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()其實是一個不帶任何修飾的函數調用,因此應用了默認綁定。
** 回調函數如 :setTimeout(obj.foo, 100) 會丟失this綁定。**
3.顯示綁定
如果我們不想在對象內部包含函數引用,而是想在某個對象上強制調用函數,該怎么做?
可以使用call(...)和apply(...)方法。第一個參數是一個對象,它們會把這個對象綁定到this, 接著在調用函數時指定這個this.
function foo(this.a){
console.log(this.a);
}
var obj = {
a: 2;
};
foo.call(obj); //2
通過foo.call(...),我們可以在調用foo時強制把它的this綁定到obj上。
bind會返回一個硬編碼的新函數,它會把參數設置為this的上下文并調用初始函數。
第三方的許多庫和js語言和宿主環(huán)境中許多新的內置函數,都提供一個可選的參數,起作用和bind(...)一樣。
4.new綁定
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); //2
使用new來調用函數時,會自動執(zhí)行下面的操作:
- 創(chuàng)建一個空對象。
- 這個對象會被執(zhí)行[ [原型] ]連接。
3.這個新對象會被綁定到函數調用的this.
4.返回這個新對象。
5. 優(yōu)先級
根據優(yōu)先級判斷函數在某個調用位置應用的是哪條規(guī)則,可以按照下面的順序來進行判斷:
- 函數是否在new中調用?如果是的話this綁定的是新創(chuàng)建的對象
- 函數是否通過call, apply(顯示綁定)
- 函數是否在某個上下文對象中調用(隱式綁定)
- 如果都不是的話,使用默認綁定。
6. ES6新東西-- 箭頭函數
箭頭函數可以像bind一樣確保函數的this被綁定到指定對象。
var obj = {
a : 2,
foo:function foo(){
setTimeout(()=>{ console.log(this.a);},100)
}
};
obj.foo(); // 2