this
this的作用是隱式的傳遞一個上下文對象。使得我們的代碼更加的優雅。
function foo(){
console.log(this.name.toUpperCase());
}
var obj = {
name : 'tom'
}
foo.call(obj); //TOM
this並不是指指向自身,學習過java等語言的童鞋應該聽過一個常說的概念,this指向調用方,即誰調用就指向誰。
下面的代碼說明this不是指向自身,但是輸出0是爲什麼?
function foo(num) {
console.log( "foo: " + num );
//記錄 foo 被調用的次數
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
} // foo: 6 // foo: 7 // foo: 8 // foo: 9
// foo 被調用了多少次?
console.log( foo.count ); // 0 -- WTF?
拋磚引玉,我們說說怎麼解決上述代碼的問題:
* this.count++改成foo.count++
* foo(i);調用改成 foo.call(foo,i);
原因我們接着看下去。
this知識點:
- this 是在運行時進行綁定的,並不是在編寫時綁定,它的上下文取決於函數調 用時的各種條件
- 調用位置和調用棧(可通過debugger在chrome控制檯觀察) this上下文對象就是調用棧倒數第二個。
function baz() {
// 當前調用棧是:baz
// 因此,當前調用位置是全局作用域
console.log( "baz" );
bar(); // <-- bar 的調用位置
}
function bar() {
// 當前調用棧是 baz -> bar
// 因此,當前調用位置在 baz 中
console.log( "bar" );
foo(); // <-- foo 的調用位置
}
function foo() {
// 當前調用棧是 baz -> bar -> foo
// 因此,當前調用位置在 bar 中
console.log( "foo" );
}
baz(); // <-- baz 的調用位置
綁定規則
- 默認綁定
*用不帶任何修飾的函數引用進行調用的,因此只能使用 默認綁定,默認綁定到全局,無法應用其他規則。
如果使用嚴格模式(strict mode),那麼全局對象將無法使用默認綁定,因此 this 會綁定 到 undefined*
function foo(){
console.log(this.a)
}
var a = 2;
foo();//2
- 隱式綁定
當函數引用有上下文對象時,隱式綁定規則會把函數調用中的 this 綁定到這個上下文對象
function foo(){
console.log(this.a)
};
var obj = {
a:2,
foo:foo
};
obj.foo(); //2
隱式丟失:
一個最常見的 this 綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用默認綁定,從而把this綁定到全局對象或者 undefined上,取決於是否是嚴格模式。
function foo(){
console.log(this.a)
}
function doSome(fn){
fn(); //這裏等價於執行foo(); 默認綁定
}
var obj ={
a:2,
foo:foo
}
var a ="test"
doSome(obj.foo) //test
我個人的竅門就是看foo()是不是乾淨的,左右有沒有別的代碼調用、包裹。沒有就是默認綁定,不管其形式是不是隱式綁定。
- 顯示綁定
你可以直接指定 this 的綁定對象,因此我 們稱之爲顯式綁定。(apply,call,bind…)
function foo(){
console.log(this.a)
};
var obj = {
a:2,
foo:foo
};
foo.call(obj);
- new 綁定
使用new進行構造函數調用的時候會執行下邊:
- 創建(或者說構造)一個全新的對象。
- 這個新對象會被執行 [[ 原型 ]] 連接。
- 這個新對象會綁定到函數調用的 this。
- 如果函數沒有返回其他對象,那麼 new 表達式中的函數調用會自動返回這個新對象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2 this指向新的對象
判斷this
現在我們可以根據優先級來判斷函數在某個調用位置應用的是哪條規則。可以按照下面的順序來進行判斷:
1. 函數是否在 new 中調用(new 綁定)?如果是的話 this 綁定的是新創建的對象。 var bar = new foo()
2. 函數是否通過call、apply(顯式綁定)或者硬綁定調用?如果是的話,this 綁定的是 指定的對象。 var bar = foo.call(obj2)
3. 函數是否在某個上下文對象中調用(隱式綁定)?如果是的話,this綁定的是那個上下文對象。 var bar = obj1.foo()
4. 如果都不是的話使用默認綁定。如果在嚴格模式下,就綁定到undefined,否則綁定到全局對象。 var bar = foo()
**一些特殊的情況**:
* 如果你把 null 或者 undefined 作爲 this 的綁定對象傳入 call、apply 或者 bind,這些值 在調用時會被忽略,實際應用的是默認綁定規則
function foo(){
console.log(this.a)
}
var a = "test";
foo.call(null); //test
- 你有可能(有意或者無意地)創建一個函數的“間接引用”,在這 種情況下,調用這個函數會應用默認綁定規則。
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
- 箭頭函數並不是使用 function 關鍵字定義的,而是使用被稱爲“胖箭頭”的操作符 => 定 義的。箭頭函數不使用 this 的四種標準規則,而是根據外層(函數或者全局)作用域來決 定 this,且不會改變
function foo(){
return (a)=>{
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 也不 行!)