這篇主要會講this的綁定,call、apply、bind,以及箭頭函式的this。
MDN: The value of this in JavaScript depends on how a function is invoked (runtime binding), not how it is defined.
JavaScript中this的值取決於函式的呼叫方式,而不是它的定義方式。
上篇的範例是函式作為物件的方法呼叫,如果是直接呼叫一般函式呢?此時this指向哪裡?測試看看:
//非嚴格模式
function showName(){
console.log(this);
}
showName(); //?
執行結果:
非嚴格模式下:在瀏覽器執行,this指向全域的window物件。
//嚴格模式
"use strict" // -->嚴格模式要加上這句
function showName(){
console.log(this);
}
showName();//undefined
在嚴格模式下:一樣在瀏覽器執行,this則是undefined。
為什麼this需要這個功能?因為this的值是動態的,會根據函式的呼叫方式有所不同,所以才需要有明確設定this的值,以防this不按預期的結果。
這三個方法可以讓我們明確的綁定this值,也就是指定this要指向哪個物件。
原先呼叫函式例如test()
,而綁定就是在函式名後面加上call / apply / bind,也就是test.call()
、test.apply()
、test.bind()
這樣。
語法
call(thisArg, arg1, arg2,..., argN)
thisArg:指定this指向的物件
arg1, arg2,..., argN:傳入該函式的參數
第一個參數放物件,指定this指向此物件:
const fruits = {
summer:"durian",
winter:"strawberry"
}
function buyFruits(){
console.log(this.summer);
}
//沒綁定
buyFruits(); //undefined
//call綁定
buyFruits.call(fruits); //"durian"
沒綁定:直接呼叫buyFruits函式,函式內的this在非嚴格模式下預設指向全域window物件,但在window物件沒有summer這個屬性,所以結果為undefined
。
call綁定:因為指定this指向fruits物件,所以可以看成console.log(fruits.summer)
,而物件裡有summer屬性,對應內容而印出"durian"。
假設第一個參數不是放物件,而是字串呢?
範例:
function buyFruits(a,b){
console.log(this,a,b);
}
buyFruits.call("夏天水果","durian","mango");
執行結果:
有發現嗎?印出來變成String物件了耶~~為什麼啊
在JavaScript中當我們把基本型別(字串、數值、布林值等)作為this使用時,他們會被包裝成物件,因為this的設計用來指向某物件。
當執行buyFruits.call("夏天水果","durian","mango")
時,JavaScript其實是把字串 "夏天水果"
轉換成 new String("夏天水果")
物件,並把字串物件作為this傳給函式。
如果都不放參數,會怎麼對應?
function buyFruits(a,b){
console.log(this,a,b);
}
buyFruits.call(); //window undefined undefined
參數為空,this預設指向全域物件。
所以在瀏覽器中,如果是非嚴格模式,對應全域物件window;在嚴格模式下對應undefined。所以在這this印出window。
apply與call差別在於傳參數的方式而已,call傳參數像一般函式,用逗號隔開;apply傳參數會用陣列包起來。
語法:
apply(thisArg, [arg1, arg2,..., argN])
thisArg:指定this指向的物件
[arg1, arg2,..., argN]:以陣列方式傳入該函式的參數
function buyFruits(a,b){
console.log(this,a,b);
}
buyFruits.apply("夏天水果",["durian","mango"]); //用陣列傳入
執行結果:
語法
bind(thisArg, arg1, arg2,..., argN)
function getScore(){
console.log(`${this.name}的分數為${this.score}分`);
}
const studentA = {
name:"小花",
score:90
}
const studentB = {
name:"小白",
score:88
}
const result = getScore.bind(studentA);
result(); //小花的分數為90分
因為bind會建立並回傳一個新函式,所以需要用一個變數來存放它。getScore.bind(studentA)
表示bind會永久綁定studentA這個物件。
即便再使用call或是apply指定別的物件,他的結果都不會變:
const result = getScore.bind(studentA);
result(); //小花的分數為90分
result.call(studentB); //小花的分數為90分
const result2 = result.bind(studentB);//物件改成studentB
result2(); //小花的分數為90分
測試結果:this指向一樣是指向小花。
也就是bind只能綁定一次,之後又在bind不會有任何影響。
MDN:
Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined.
也就是說父層的this是什麼,箭頭函式的this就是什麼。
However, arrow functions do not have their own this binding.
箭頭函式沒有自己的this綁定。
重點:
範例:
const obj = {
name:"test",
func:function (){
console.log("func",this);
},
arrowFunc:() => {
console.log("arrowFunc",this)
}
}
obj.func(); //this指向obj,印出obj內容
obj.arrowFunc(); //指向window
執行結果:
在obj物件中,有兩個屬性,func屬性內容放一般函式,arrowFunc放箭頭函式。
兩者呼叫,可以看到this的指向不同,obj.func()
指向到obj,印出obj內容;obj.arrowFunc()
則指向到全域window物件。
以上分享~謝謝!
MDN - this
Chris 技術筆記-this 決策演算法 - 什麼是 this
What's THIS in JavaScript ? [上][中][下]
JS 原力覺醒 Day17 - this 的四種繫結
淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
JavaScript 忍者秘籍, 2/e (Secrets of the JavaScript Ninja, 2/e)