iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 22
0

This 的動機與用處

先試試看能不能了解 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 更為雜亂。

容易混淆之處

有兩個,把它當成 ItselfIt's Scope 都是不正確的。

自身 ( Itself )

這是相當合理的推論。在這個函式內用 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

那如果我真的就是想要指定到 itself 呢?

具名函式可以,匿名函式不行( 可以的方案也棄用了 )。
但還是處在仰賴語彙範疇的階段。

function foo() {
    foo.count = 4; // foo 自身
}

那如果我真的就是想要用 this 指向 itself 呢?

用 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 有點破題。)

其範疇 (It's Scope)

會這樣想的顯得比較高竿一點。因為是真的很像。

還是看程式碼吧。

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 是什麼?

this 不是工程師自己定義的繫結。
他會依照當下的函式調用時,才進行繫結。

函式調用時,會有一筆啟動紀錄( activetion record ),或稱為執行情境 ( execution context )。
此筆紀錄含有的資訊包含

  1. 從何處被呼叫 ( call-stack )
  2. 該函數如何被調用
  3. 傳入了什麼參數 ( parameters )

參考資料

  1. 你所不知道的JS
  2. 克服 JavaScript 奇怪的地方

上一篇
Day21 - 運用閉包模組化
下一篇
Day23 - This 現在全都說得通了!
系列文
你為什麼不問問神奇 JavaScript 呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言