先試試看能不能了解 this 的意思吧。
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call( this );
console.log( greeting );
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify.call( me ); // KYLE
identify.call( you ); // READER
speak.call( me ); // Hello, I'm KYLE
speak.call( you ); // Hello, I'm READER
真的跟書上講的,如果不知道如何運作會很困擾。
identify.call( me ); 會呼叫 identify 並使用 call 把 this 指向 me。所以 identify 的 this 會因此指向 me,而把 name UpperCase。
speak.call( me ); 會呼叫 speak 並使用 call 把 this 指向 me。所以 speak 內的 this 會指向 me。
然後接著在執行的過程中。又遇到 identify.call( me ),就重複上面的步驟。
這時候,它提供了另外一個不用 this 的方案。
利用函式的參數傳遞資訊。
但這個方案在專案龐大之後,都會比 this 更為雜亂。
有兩個,把它當成 Itself
和 It's Scope
都是不正確的。
這是相當合理的推論。在這個函式內用 this 不就是指函式本身嗎?
開始討論為什麼不正確吧。下面是一個計算 count 跑了幾次的模擬。
function foo(num) {
console.log( "foo: " + num );
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
console.log( foo.count ); // 0
可以看到 當 i > 5 時,就會執行 foo(),所以會得到 6, 7, 8, 9 到 10 跳出迴圈。
foo() 共執行了 4 次,那 this.count++
也應該要加了 4 次。
這個 this 不是 foo 嗎? 對,他證明了。
那 this 是什麼?
funciton bar(){
console.log(this, "Bar 的 this");
}
console.log(this, "Global 的 this");
bar();
這兩者回傳的都會是 window 的物件。
不用 this 如何解決呢?
你可以像第一個範例一樣,給予一個物件的字面值,並且利用語彙範疇,對物件內的值做變更。
function foo(){
data.count++;
}
var data = {
count: 0
}
foo();
console.log(data.count); // 1
foo();
console.log(data.count); // 2
具名函式可以,匿名函式不行( 可以的方案也棄用了 )。
但還是處在仰賴語彙範疇的階段。
function foo() {
foo.count = 4; // foo 自身
}
用 call 吧!
function foo(num) {
console.log("foo: " + num);
this.count++;
}
for ( var i = 0; i < 10; i++){
if ( i > 5 ) {
foo.call( foo, i); // 第一個是參考,後面的是參數
}
}
之後會解釋 call 的原理。( 雖然一開始 Tony 有點破題。)
會這樣想的顯得比較高竿一點。因為是真的很像。
還是看程式碼吧。
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); // undefined
執行 foo(),如果 this 是範疇,那就會包含內部所有的參數。
如果 bar 處在同樣的範疇,在 RHS 時,就會往上找到 foo。
這樣可以輸出 a 吧。
事與願違。
兩個 this 都是指向 window,可以當作是全域。
所以 bar 在 this 下,也可以使用。
bar 裡的 a 並沒有在全域宣告,就找不到。
this 不是工程師自己定義的繫結。
他會依照當下的函式調用時,才進行繫結。
函式調用時,會有一筆啟動紀錄( activetion record ),或稱為執行情境 ( execution context )。
此筆紀錄含有的資訊包含
- 從何處被呼叫 ( call-stack )
- 該函數如何被調用
- 傳入了什麼參數 ( parameters )