Day 16:this 關鍵字 (2)
函數執行環境下 (Function Context) (續)
2. 簡易呼叫 (Simple Call)
this
物件:
一般模式下:Global 物件。
嚴謹模式下:
undefined
簡易呼叫指的是函數被單獨呼叫,前面沒有帶任何呼叫物件的情境。
例如這樣的語法:
當函數被單獨呼叫,無論呼叫地點在全域環境還是函數環境內,此時執行這段程式的預設綁定擁有者是 Global 物件。
需要注意的是,這是指一般模式下的行為,一定會設定一個綁定物件,因為程式碼沒有指定呼叫物件,Global 物件就被推派出來當預設綁定者。
但基於安全性上的考量,嚴謹模式下不會作呼叫物件的強制給予,也就是說不會預設綁定 Global 物件當呼叫物件,程式碼沒指定就當沒有,因此 this
會是 undefined
。
節錄 W3Schools 的原文:
When used alone, the owner is the Global object.
The Global object (the owner of the function) is the default binding.
Strict mode does not allow default binding.
根據函數被呼叫的地點不同,有幾種情境。
2.1. 全域環境 (Global Context) 下定義函數 & 呼叫函數
一般模式下,this
會綁定 Global 物件,在 HTML 環境裡就是 window
物件:
在嚴格模式下,this
不會作預設綁定,會是 undefined
:
但其實對簡易呼叫來說,在哪裡被定義和呼叫都不重要,再看下一個例子會更明白。
2.2. 內部函數 (Inner Functions)
內部函數是指在 A 函數內定義一個 B 函數,然後在 A 函數裡呼叫 B。
例如以下例子 (一般模式):
執行結果:
這個例子是在 obj.f()
內再定義一個內部函數 foo()
,進行內部呼叫。
如前面所說,foo()
沒有指定呼叫物件,this
就是 Global 物件,因此 foo()
所印出的 this.x
值會是全域變數的 x
,而非 obj
的 x
(Output 2)。
如果這裡想讓 foo()
可以取到obj.x
,可以使用一個變數去儲存執行 obj.f()
時的 this
物件。
執行結果:
3. HTML 事件處理 (HTML Event Handlers)
this
物件:接受該事件的 HTML 元素 (HTML Element)。
在 HTML 元件的事件 Callback 裡,this
就是該事件的 HTML 元素。
例如下面例子,onclick
裡的 this
,指的就是 <button>
元素本身。
4. 顯性函數綁定之 bind() 篇 (Explicit Function Binding for bind())
this
物件:新函數物件被指定的綁定物件,也就是Function.prototype.bind()
的第一個參數。
ES5 導入了 Function.prototype.bind
,可以為一個函數建立一個繼承該函數 prototype 的新函數物件,但綁定一個固定的擁有者。
換句話說,無論新的函數物件怎麼被呼叫,函數內的 this
都會是當初綁定的那個擁有者物件。
此外,透過 Function.prototype.bind
的綁定,一般模式或嚴謹模式是一樣的結果。
4.1. 一般模式下的範例
例如下面的例子:
上面例子在全域環境 (Global Context) 定義了函數 getFullName()
:
透過簡單呼叫去執行
getFullName()
,也是上面 2.1 節所舉的情境。在一般模式下會預設綁定 Global 物件作為
this
值。因此函數內會找到全域變數
firstName
和lastName
,因而印出"One Jar"
。
使用
getFullName().bind()
,分別產生了兩個新的函數物件,繼承了getFullName()
的 prototype,但各自綁定了固定的擁有者物件。introIronMan
函數物件綁定了擁有者物件{ firstName: "Tony", lastName : "Stark" }
。introCaptainAmerica
函數物件綁定了擁有者物件{ firstName: "Steven", lastName : "Rogers" }
。
一樣透過簡單呼叫的形式去呼叫
introIronMan()
和introCaptainAmerica()
,函數內的this
值會是各自當初綁定的物件 (而非 Global 物件)。
4.2. 嚴謹模式下有同樣的行為
上面執行
getFullName()
時,因為是嚴謹模式,this
不再預設綁定 Global 物件,因此是undefined
(2.1 節情境)。introIronMan()
和introCaptainAmerica()
不受影響。
4.3. Binding 就像山盟海誓,只有第一次有效
需要注意的是,對一個函數物件來說,只有第一次的 Binding 動作有效。
透過 Function.prototype.bind
所建立的新函數物件 A,如果企圖用 Function.prototype.bind
再去建立一個新函數物件 B 並綁定新擁有者,因為函數物件 B 繼承了 A 的 prototype,包含當初的綁定者,因此無論是否給予新的綁定對象都沒有用。
但第二次綁定的當下並不會拋錯,只是沒有效果,即使在嚴謹模式下也不會 (感覺這也是嚴謹模式的遺珠)。
References
Last updated