JavaScript #9 — This (上)
This
This 是什麼?This 指向誰?This 的型別又是什麼?先來簡單說明一下 :
- This 是在 JavaScript 中的關鍵字。
- This 是在 函式(Function)執行時所產生的內部物件。
- This 是依 函式 (Function) 執行時機環境不同,其指向的物件也有所不同。
- This 在嚴格模式下會回傳
undefined
。 - This 在 DOM element 的監聽事件中指向觸發節點元素。
以上說明了這麼多,This 在不同的時機環境下,產生結果也有所不同。
預設綁定 (Default Binding)
在全域環境下的變數或函式沒有特別綁定 this
的狀況下,會指向預設綁定中的 window ,也就是全域物件(屬性)。
this === window //true
再來看看 函式的範例 :
function foo(){
console.log(this)
}foo() //window
沒有錯, foo()
函式在全域環境下執行時,this
會預設指向 window 。
var a = 'outer';function foo() {
console.log(this.a);
function boo() {
console.log(this.a);
function bar() {
console.log(this.a);
}
bar(); //'outer'
}
boo(); //'outer'
}
foo(); //'outer'
不管在任何一層呼叫函式都是指向 全域 window.a 物件。
但是在嚴格模式下,this
的值就會改為 undefined
,而不是 window。
顯式綁定 (Explicit Binding)
那如果要明確的指定 this
指向某個物件呢?
在 函式 (Function)中有三個方法可以明確的指定 this
要綁定哪個物件,分別是 :call()
、apply()
、bind()
。
bind( )
在以下的範例中,執行 obj.funcA()
函式可以發現到 funcA()
中的 this.word
指向 obj
物件中的 word
,但 funcB()
中的this.word
是指向全域物件中的 age
(以 var
宣告變數為 全域的屬性 參考 : JavaScript #1 — 運算子、運算式、值與型別、變數
),因執行 funcB()
函式時,並沒有明確指定 this
要綁定哪個物件,會依照上述的 預設綁定給 全域物件 (window),那就是 window.word
回傳值為 35。
var word = 'DEF'var obj = {
word: "ABC",
funcA: function () {
console.log(this.word) //ABC
function funcB() {
console.log(this.word) //DEF
}
funcB()
}
}obj.funcA()
那如果要強制綁定 this
呢!可以使用 bind()
方式綁定。
var word= 'DEF'var obj = {
word: "ABC",
funcA: function () {
console.log(this.word) //ABC
function funcB() {
console.log(this.word) //ABC this.word 指向 obj 中的 word
}
funcB.bind(obj)() //使用 bind() 方式綁定
}
}obj.funcA()
在執行 funcB()
函式時先使用 bind()
綁定 this
的物件為 obj
,之後再 funcB()
函式中的 this.word
就會暫時強制指向 obj
物件中的 word
,傳回結果為 ABC
。
call( ) & apply( )
再來說明這二種方法,那使用以下範例來說明 :
let obj = {
word: 'ABC',
foo: boo,
};function boo() {
console.log(this.word);
}obj.foo(); //ABCboo(); //undefined
在執行 obj
物件中的 foo
函式 為 隱含式綁定(這部份之後會再提到),所以 foo
函式中的 this.word
會指向 obj
物件中的 word
,回傳結果 ABC
。
但執行 boo()
函式時,確回傳值為 undefined
,就如同之前的 預設綁定方式,在全域環境下執行 boo()
函式時, 函式中的this.word
會指向全域物件的 window.word
變數,可是在全域物件並沒有word
變數,則會回傳值為 undefined
。參考 : JavaScript #1 — 運算子、運算式、值與型別、變數
這時就可以使用 call()
、apply()
來強制綁定 this
的物件如以下範例 :
function Foo(name) {
console.log(` Name: ${name} / Age : ${this.age}`)
}let obj = {
age: 33
}//Name: ABC / Age : 33
Foo.call(obj, 'ABC')
//Name: CDE / Age : 33
Foo.apply(obj, ['CDE'])
如上述範例中,在呼叫 Foo()
函式前先以 call()
或apply()
來強制綁定 this
的物件為 obj
,回傳的結果就如上述的一樣。
這時會發現,這兩種的方式不同之處,call()
、apply()
中第一個引數為 thisArg
,強制指定某個物件作為該 function
執行時的 this
,之後只差別要輸入函式的參數有所不同而以,call()
以 逗號隔開每個傳入的參數,而 apply()
則是傳入 陣列為參數。
call() 、apply()、bind() 差異
- bind() : 在呼叫函式前先綁定
thisArg
的物件,並在執行時都能有固定的this
物件 (特性 用來建立新的函式)。 - call() 、apply() :會依照執行當下的不同並帶入不同所需
thisArg
的物件 (特性 當下執行函式),且thisArg
較常變動的當下。
參考 : JavaScript Function (函式)-學習筆記
隱含式綁定 (Implicit Binding)
在全域環境下,如呼叫函式 (Function) 為物件中的方法,執行函式時 this
就會綁定該物件。
let obj = { number: 37 };function getNum() {
return this.number
};obj.func = getNum;console.log(getNum()); //undefinedconsole.log(obj.func()); // 37
從此範例來看,當 getNum()
在全域環境下呼叫時,函式中的this.number
會指向全域物件的 window.number
變數,但在全域物件中並沒有number
變數,則會回傳值為 undefined
,此為 預設綁定。
當我們在 obj
物件中取得 func
屬性時,會呼叫全域環境下的 getNum()
函式,當中 this.number
的 this
會指向 obj
物件(上層的物件為 obj ),於是此時 obj.func()
的值為 37。
以上為 隱含式綁定 (Implicit Binding) 。
再來看看另個範例 :
let obj2 = { number: 7, obj2Num: getNum }let obj1 = { number: 37, obj1Num: obj2 }function getNum() {
return this.number
}obj1.obj1Num.obj2Num() //7 指向 obj2 物件中的 number
當 obj1
物件取得 obj1Num
屬性時,此屬性會去取得 obj2
中在全域環境下的 getNum()
函式,並將 this.number
指向 obj2
中的物件,所以回傳值為 7,由此可見 決定 this
的時機點(是在被呼叫的時候決定)是上一層的物件才是有用的。(正好相反於 範圍鏈 (Scope Chain) 當中變數是 被定義的當下決定 )。參考 : JavaScript #5 — 閉包(Closure)、範圍鏈(Scope Chain)
…
參考:鐵人賽:JavaScript 的 this 到底是誰?、this 、 JavaScript this 、The JavaScript this Keyword
以上是我對 JavaScript #9 — This (上) 的學習筆記 😉。
***如果有任何想法,也歡迎留言與我分享~***