講了這麼久,終於可以來講 this
這個大魔王了
物件導向中的 this
在物件導向中,this 就代表現在對應到的 instance
class Dog{
setName(name){
this.name = name
}
sayHello(){
console.log(this.name + ': hello')
}
}
const yoyo = new Dog(‘yoyo')
yoyo.sayHello()
像是上面這個 this,指的就是 yoyo
其他地方的 this
那在不是物件導向的地方使用 this
基本上就會是預設值,依照執行環境而有所不同
function test() {
console.log(this)
}
test()
例如在 Node.js,這個 this 就是 global
在瀏覽器,這個 this 就是 window
但在嚴格模式下(最前面加上 'use strict’;
),不論執行環境,this 都是 undefined。
-
但有幾種情況例外
(1) 在使用 DOM 元素時,this 就會是你點的按鈕
document.querySelector('.btn').addEventListener('click', function() {
console.log(this)
})
(2) 使用 call & apply 呼叫 function 的時候
前面說過用 .call
去呼叫 function,帶入的第一個參數,可以改變 this
function test() {
console.log(this)
}
test.call(123)
apply 也和 call 有一樣的功能,差別只在於後面帶入的參數要用陣列
function test(a, b, c) {
console.log(this)
console.log(a, b, c)
}
test.call(123, 1, 2, 3)
test.apply(123, [1, 2, 3])
this 跟如何被呼叫有關
有趣的是,this 的值不在於他在程式碼的哪裡,而是和 function 如何被呼叫有關。
用以下程式碼舉例
const obj = {
a: 123,
test: function(){
console.log(this)
}
}
obj.test()
這邊的 this 是 obj
那如果換一種呼叫方式
var func = obj.test
func()
這邊的 this 卻是 undefined
提供一個有趣判斷 this 的解法
obj.test()
可以看成 obj.test.call(obj)
=> this 就是 obj
obj.inner.test()
可以看成 obj.inner.test.call(obj.inner)
=> this 就是 obj.inner
那剛剛的 func()
可以看成 func.call()
=> this 因為沒傳東西進去就會是 undefined
固定 this
那有辦法固定 this 的值嗎?有,使用 bind
剛剛我們說到
var func = obj.test
func()
this 會得到 undefined
但如果我們改寫成
var func = obj.test.bind(‘aabbcc’)
func()
此時的 this 就會被我們綁定為 aabbcc
之後就算我們用 call or apply 都無法去改變 this 的值。
this 的特例 again?
最後要來講一個特例
先看個程式碼
class Test{
run() {
console.log('run this:', this)
setTimeout(function() {
console.log(this)
}, 100)
}
}
const t = new Test()
t.run()
第一個 this 是 t,沒什麼問題
第二個 this,只是一般執行 function,所以如果用瀏覽器就是 window
但如果換一個寫法,換成 arrow function
class Test{
run(){
console.log('run this:', this)
setTimeout(() => {
console.log(this)
}, 100)
}
}
const t = new Test()
t.run()
就會發現第二個 this 也變成 t 了!
arrow function 的 this 會使用定義時的 this,所以就會跟第一個 this 一樣是 t,在使用時也要特別注意。
更多的 this 可以看 Huli 大的:淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
或是What's THIS in JavaScript ?系列文,共三篇