誰是 this ?
簡單說
- 誰調用了包含
this
的函式,誰是就this
! - 呼叫 函式 的環境,就是
this
全域環境
在 全域 環境下 this
等於 全域物件 (this === window
)
js
// 在網路瀏覽器中,window 物件也是全域物件
console.log(this === window) // true
// 全域變數 a 賦值
var a = 123 // this.a = 123 一樣意思
console.log(window.a) // 123
函式環境
呼叫 函式 的環境,就是 this
!
js
function sayThis() {
return this
}
瀏覽器 / Node
this
就是window
(全域物件)this
就是global
(全域物件/對象)
js
// 瀏覽器
sayThis() // window
// node
sayThis() // global
嚴格模式
在 嚴格模式 的情況下,會禁止 this
為 全域物件,這時 this
若為 全域物件 就會顯示 undefined
js
'use strict'
function sayThis() {
console.log(this)
}
sayThis() // undefined
物件環境
圖片出處: 卡斯伯's Blog
物件內 函式 的 this
會指向執行時所屬的物件 本身。下面的例子 this
就是分別為呼叫函式的物件 person1
與 person2
。
js
function getGender() {
return this.gender
}
let person1 = {
gender: 'male',
getGender: getGender
}
let person2 = {
gender: 'female',
getGender: getGender
}
// (this === person1)
person1.getGender() // male
// (this === person2)
person2.getGender() // female
如果有多層 函式 的 this
都是指向上一層!
js
function sayMyName() {
console.log(this.name)
}
const person = {
name: 'nike',
age: 34,
sayName: sayMyName,
children: {
name: 'niki',
age: 6,
sayName: sayMyName
}
}
// ↓ this === person
person.sayName() // nike
// ↓ this === children
person.children.sayName() // niki
呼叫函式的環境 決定 this
js
var name = 'out'
function sayName() {
console.log(this.name)
}
const person = {
name: 'in'
}
person.sayName = sayName
// 呼叫函式的環境是 `person`
person.sayName() // in
// 呼叫函式的環境是 `window` (全域物件)
sayName() // out
間接呼叫
承上,一般操作 person.sayName()
就會視 person
為 this
;但如果將 person.sayName
賦與給 全域變數 a
,再經由 全域變數 來呼叫 函式 ,那函式的呼叫環境,就是 「全域物件 window
」。
🔰 呼叫函式的環境 決定 this
!!
js
person.sayName() // in
const a = person.sayName
a() // out
脫離物件的 this
fun1
函式中的 this
就是指向 person
就如之前的理解, 但 fun2
的 this
為 window
,就與認知不同了。
js
const person = {
name: 'nike',
fun1: function() {
console.log(this === person) // this 指向 person
let fun2 = function() {
console.log(this === person) // this 指向 window
}
fun2()
}
}
person.fun1() // true false
「脫離了物件, this
的值就沒什麼意義」
DANGER
當 函式 (含 this
) 脫離 物件 時, this
就是 全域物件
箭頭函式的 this
箭頭函式不擁有 this
特性,會指向 全域物件。
js
var name = 'out'
const person = {
name: 'in',
sayName: () => {
console.log(this.name)
}
}
person.sayName() // out
指定 this
在 嚴格模式 下,this
若指向 全域物件 則為 undefined
。
js
'use strict'
function person(age, count) {
console.log(this, age, count)
}
person(34, 4000) // undefined 34 4000
call
定義函式執行時的 this
值,同時傳入 參數
。 MDN
fun.call(thisArg[, arg1[, arg2[, ...]]]
)
thisArg
操作fun
函式時,指定的this
arg1
… 其它參數
js
person.call(undefined, 34, 3000) // undefined 34 3000
person.call({name: 'nike'}, 34, 3000) // {name: 'nike'} 34 3000
範例
一般來說 sayName2
函式執行的 this
會是 window 全域物件,但可以使用 call
來指定 this
js
const person = {
name: 'nike',
age: 34,
sayName: function() {
console.log(this.name)
let sayName2 = function() {
console.log(this)
}
sayName2() // window
sayName2.call(this) // 把 person 傳入當 this
}
}
person.sayName()
// nike window {name: 'nike', age: 34, sayName: ƒ}
apply
這個操作與 call
其本上是全部相同,只差別在 參數
要使用 陣列 來傳入。 MDN
fun.apply(thisArg, [argsArray]
)
thisArg
操作fun
函式時,要傳入的this
argsArray
其它參數,使用 陣列 傳入
js
person.apply(undefined, [34, 4000]) // undefined 34 4000
person.apply({name: 'niki'}, [34, 4000]) // {name: 'niki'} 34 4000
bind
與 call
操作方式 87 分像,只是 bind
會回傳一個綁定好 this
的 函式,待後續執行。 MDN
fun.bind(thisArg[, arg1[, arg2[, ...]]]
)
thisArg
要綁定的this
arg1
... 要傳入的參數
js
const bindPersonThisFun = person.bind(undefined, 34, 4000)
bindPersonThisFun() // undefined 34 4000
const bindPersonThisFun2 = person.bind({name: 'nike'}, 34, 4000)
bindPersonThisFun2() // {name: 'nike'} 34 4000
何時會使用到綁定?
1. 物件屬性方法內再宣告,有 this 函式
此時宣告的函式內 this
為 全域物件 就不再是這個物件,可以使用 綁定 this 來解決這個問題。
js
const person = {
name: 'nike',
fun1: function() {
console.log(this === person) // this 指向 person
let fun2 = function() {
console.log(this === person) // this 指向 window
}
fun2()
}
}
2. 建構函式的方法函式內再宣告,有 this 函式
函式內再宣告,有 this
的函式,這個 this
會指向 全域變數。
js
class Person {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
sayNameAgin() {
setTimeout(function() {
console.log(this.name)
}, 1000)
}
}
const niki = new Person('NIKI')
niki.sayName() // NIKI
niki.sayNameAgin() // undefined
解決方法:
- 使用 箭頭函式js
setTimeout(() => { console.log(this.name) }, 1000)
- 使用 綁定
this
方法jssetTimeout(function() { console.log(this.name) }.bind(this), 1000)
參考: