想像一下,今天你是負責幫忙點菜的服務生,來了一桌客人,拿著菜單跟你說 「 我要 this this this ! 」,請問你知道他點三小什麼嗎,相信除非你會通靈不然光聽絕對猜不出來,那怎麼判斷呢?
看客人的手指向哪道菜判斷那個 this 對應誰
在 JavaScript 中也會遇到這種不直接告訴你 this 是誰的情況,雖然這裡沒有手指頭做參考,但別怕,教你另一招絕對精準的判斷方式,包你以後一秒指認 this !
怎麼看出 this 是誰? 從呼叫方式判斷
不同的呼叫方法下的 this 會不相同,呼叫方式可分為以下四種:
接下來就一一從每個呼叫方法來認識 this 是誰吧!
最直接的函式呼叫方式,就是在函式名稱後加上一個 ()
不論是 function declaration 或 function expression 或 立即函式 都算在一般的函式呼叫內。
這時的 this 是誰:在一般函式呼叫下的 this 代表的是全域,在嚴格模式下則是 undefined。
//函式宣告
function func(){
console.log(this)
}
func() // window
//函式表達式
let func = function(){
console.log(this)
}
func() // window
//立即函式
(function (){ console.log (this)})()
當函式成為一個物件內的屬性時,就可以稱該函式為一個 方法method,呼叫的語法像是 物件名稱.函式名稱()
這時的 this 是誰:作為 method 呼叫下的 this 會指向 method 所屬的物件。
var dish = '黃金開口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鮮鯛山',
orderMore: order
}
let customer2 = {
dish: '奇蹟彗星炒飯',
orderMore: order
}
// 以下請自行加上 console.log
customer1.orderMore() // '烈冰鮮鯛山' ,this 指向 customer1
customer2.orderMore() // '奇蹟彗星炒飯',this 指向 customer2
order() // '黃金開口笑', 一般函式呼叫 this 指向全域
變化題,若是將 customer1.orderMore
這個 method 存入一個變數中再進行呼叫呢?
let secondDish = customer1.orderMore; // 將 customer1 的 orderMore 存入另一個變數再呼叫
secondDish() // 黃金開口笑,此時的呼叫屬於一般函式呼叫,this 指向全域
// 除非是將使用 method 呼叫後的結果存入變數,該變數才會是 this 指向物件的結果
function order() {
return (this.dish)
}
let secondDish = customer1.orderMore();
console.log( secondDish ) // 烈冰鮮鯛山
上一篇講到使用關鍵字 new
和建構式 constructor 創物件,這時的 this 會指向這個新創的物件,詳見 D13 - 做出雞蛋糕 new + Constructor
這時的 this 是誰:使用 new + constructor 下的 this 是新創的那個物件。
function Order(dish) {
this.dish = dish
}
let customer1 = new Order('烈冰鮮鯛山')
let cusomter2 = new Order('奇蹟彗星炒飯')
console.log( customer1 ) // Order {dish: "烈冰鮮鯛山"}
console.log( customer2 ) // Order {dish: "奇蹟彗星炒飯"}
點菜時除了依照客戶的喜好,服務生也可以推薦菜色,讓客人更改 「this」的選項為餐廳主打的招牌菜。
JavaScript 內也可以指定我們要的 this,透過所有函式都具備的兩種方法:apply 和 call,在呼叫函式的同時,在參數內放入希望 this 指向的物件。
這時的 this 是誰:使用 call 和 apply 小括號內放的第一個引數。
var dish = '黃金開口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鮮鯛山',
orderMore: order
}
let customer2 = {
dish: '奇蹟彗星炒飯',
orderMore: order
}
customer1.orderMore.call(customer2) // 奇蹟彗星炒飯,強制 this 指向 customer2
customer2.orderMore.apply(window) // 黃金開口笑,強制 this 指向全域 window
order() // 黃金開口笑, 一般函式呼叫 order, this 指向全域
order.call(customer1) // 烈冰鮮鯛山, 透過 call 綁定 this 指向 customer1
兩者唯一的差異是引數的格式,第一個都是放 this 指定的物件,剩下的放欲帶入函式的引數
function.call ( 指定 this 的物件, 引數1, 引數2, 引數3)
function.apply ( 指定 this 的物件, [引數1, 引數2, 引數3])
call 可以接受任一數量的引數,適用在有多個彼此不相關的變數或實質
apply 的引數須包在陣列內
除了上面 call 和 apply 可以指定 this,另外兩種綁定 this 的方式為: bind 和 箭頭函式
為什麼要分開寫呢? 因為這兩種並不會進行函數呼叫,無法從呼叫判定,兩者的 this 都是在定義時就綁定好
bind 的 this 綁定跟 apply 和 call 一樣,在 ()
中放入 指定的 this 物件
這時的 this 是誰:bind 依照小括號內放置的物件
var dish = '黃金開口笑'
function order() {
console.log (this.dish)
}
let customer1 = {
dish: '烈冰鮮鯛山',
orderMore: order// 綁定 this 為 customer 2
}
let customer2 = {
dish: '奇蹟彗星炒飯',
orderMore: order.bind(customer1)
}
customer2.orderMore() // 烈冰鮮鯛山
customer2.orderMore.call(customer2) // 烈冰鮮鯛山,無法透過 call, apply 重新指定 this
let specialMenu = order.bind({dish: 'JS吃到飽'}) // 綁定 order function 內的 this
specialMenu() // JS吃到飽
箭頭函式為 ES6 加入的 function expression 新寫法
箭頭函式下的 this 會指向創建時的物件,一般幾乎指向全域,除非是使用建構式生成時,箭頭函式的 this 指向新創的物件,因此在箭頭函式內使用 this 要特別留意,一但定義了就無法重新綁定囉!
這時的 this 是誰:箭頭函式下的 this 可能指向全域或是創建的物件。
var dish = '黃金開口笑'
let customer1 = {
dish: '烈冰鮮鯛山',
orderMore: ()=> this.dish,
orderMoreMore: function(){
return (this.dish)
}
}
let customer2 = {
dish: '奇蹟彗星炒飯',
}
// 雖然使用 method 方式呼叫,但因 orderMore 使用箭頭函式, this 已綁定在全域
customer1.orderMore() // 黃金開口笑
customer1.orderMore.call(customer2) // 黃金開口笑,綁定在全域下無法透過 call、apply 更改 this
// 一般匿名函式的 this 不會先被綁定,隨呼叫不同而改變
customer1.orderMoreMore() // 烈冰鮮鯛山
customer1.orderMoreMore.call(customer2) // 奇蹟彗星炒飯
一般情況下的 this 都會指向全域,但在 constructor 內使用箭頭函式的 this 可以綁定在新物件上
function Order(dish) {
this.dish = dish;
this.orderMore = ()=> this.dish
}
let customer1 = new Order('烈冰鮮鯛山')
let customer2 = new Order('奇蹟彗星炒飯')
customer1.orderMore() // 烈冰鮮鯛山,箭頭函式內的 this 指向新建立的物件 customer1
customer2.orderMore() // 奇蹟彗星炒飯,箭頭函式內的 this 指向新建立的物件 customer2
以後遇到 this 想知道它到底指向誰,記住先看它是怎麼被呼叫的,再次整理如下
[008重新認識 JavaScipt]
忍者 JavaScript 開發技巧探秘第二版 by John Resig, Bear Bibeault, Josip Maras
讚讚~一直有小當家的畫面跑出來
一般匿名函式的 this 不會先被綁定,隨呼叫不同而改變
學到一個新觀念~謝謝!!!