參考資料:What's THIS in JavaScript ?
- 面試回答:
let obj = {
x: 1,
test: function () {
console.log(this.x);
},
};
obj.test(); // 1
// this 會指向呼叫這個函式的物件,也就是 obj 本身
// 等於 obj.test.call(obj)
- this 是 JavaScript 的一個關鍵字。
- this 是 function 執行時,自動生成的一個內部物件。
隨著 function 執行場合的不同,this 所指向的值,也會有所不同。 - 在大多數的情況下, ==this 代表的就是呼叫 function 的物件== (Owner Object of the function)。
var getGender = function(){
return this.gender;
};
var people1 = {
gender: 'female',
getGender: getGender
};
var people2 = {
gender: 'male',
getGender: getGender
};
console.log( people1.getGender() ); // female
console.log( people2.getGender() ); // male
==this 會因執行的環境與上下文 (context) 的不同,而有不同的結果。==,this 代表的是 function 執行時所屬的物件。
- 在物件導向中,this 用來存取對應的 instance,也就是代表在物件導向裡面,那個 instance 本身。
class Car {
setName(name) {
this.name = name
}
getName() {
return this.name
}
}
const myCar = new Car()
myCar.setName('hello')
console.log(myCar.getName()) // hello
在上面宣告了一個 class Car
,寫了 setName 跟 getName 兩個方法,在裡面用this.name
來存取這個 instance 的屬性。
==this 的作用就是所指到的對象就是那個 instance 本身。==
以上面的範例來說,myCar.setName('hello')
,所以 this 就會是myCar
。
一但脫離了物件導向,其實 this 就沒有什麼太大的意義
在沒有意義的地方呼叫 this,預設值會是什麼?
function hello(){
console.log(this)
}
hello()
- 嚴格模式
use strict
底下就都是undefined
- 非嚴格模式,瀏覽器底下是
window
- 非嚴格模式,node.js 底下是
global
直接印出 this 會是一個空物件
var a = '10'
console.log(this) // {}
console.log(this.a)// undefined
console.log(10 == this.a) // false
console.log(10 === this.a) // false
更改 this 的值
call & apply
'use strict';
function hello(a, b){
console.log(this, a, b)
}
hello(1, 2) // undefined 1 2
hello.call(undefined, 1, 2) // undefined 1 2
hello.apply(undefined, [1, 2]) // undefined 1 2
call 跟 apply 的差別就是,call 跟平常呼叫 function 一樣傳入 function 的參數,apply 的第二個參數則是將 function 的參數用 array 包起來。
至於第一個參數傳什麼,裡面 this 的值就會是什麼。
'use strict';
function hello(a, b){
console.log(this, a, b)
}
hello.call('yo', 1, 2) // yo 1 2
hello.apply('hihihi', [1, 2]) // hihihi 1 2
儘管原本已經有 this,也依然會被這種方法給覆蓋掉:
class Car {
hello() {
console.log(this)
}
}
const myCar = new Car()
myCar.hello() // myCar instance
myCar.hello.call('yaaaa') // yaaaa
bind
'use strict';
function hello() {
console.log(this)
}
const myHello = hello.bind('my')
myHello() // my
與 call 和 apply 不同,==bind 會回傳一個新的 function==,在這邊我們把 hello 這個 function 用 my 來綁定,所以最後呼叫 myHello() 時會輸出 my。
小總結
- 在物件以外的 this 基本上沒有任何意義,硬要輸出的話會給個預設值
- 可以用 call、apply 與 bind 改變 this 的值
物件中的 this
重點注意:
this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「你如何呼叫」有關
const obj = {
value: 1,
hello: function() {
console.log(this.value)
}
}
obj.hello() // 1
const hey = obj.hello
hey() // undefined
明明就是同一個函式,怎麼第一次呼叫時 this.value
是 1,第二次呼叫時就變成 undefined 了?
==function call 呼叫方式補充:==
在 ES5 中正常情況下的 function call:
func.call(context, p1, p2)
,其中第一個參數 context 就是 this
"use strict"
function test() {
console.log(this)
}
test() === test.call() === test.call(undefined)
// strict 模式下是 undefined
// 在 node.js 是 global, 在瀏覽器是 window
test.call("ass") // 第一個參數傳的值就是 this
// ass
---
var obj = {
test: function() {
console.log(this)
}
}
obj.test() === obj.test.call(obj)
// {test: [Function: test]} 就是 obj 自己
參考資料:this 的值到底是什么?一次说清楚
這時把所有的 function call,都轉成利用call
的形式來看,以上面那個例子來說,會是這樣:
const obj = {
value: 1,
hello: function() {
console.log(this.value)
}
}
obj.hello() // 1
obj.hello.call(obj) // 轉成 call,印出 1
// obj.hello() 可以看成 obj.hello.call(obj)
// 括號內的 obj 就是 this
const hey = obj.hello
hey() // undefined
hey.call() // 轉成 call 來看,沒傳參數,所以是 undefined
在非物件導向、非嚴格模式下,呼叫 hello 函式,依舊將其轉為 call 的形式來看。
在不同的執行環境 (runtime) 下,傳入 call 第一個參數的值不同,在瀏覽器下是 window,在 node.js 下是 global,所以轉為 call 的形式變成:hello.call(window)/hellow.call(global)
因已將 hello 賦值為 obj.inner.hello,hello() 會執行 console.log(this.value),欲印出 this.value 的值。而無論 runtime 是瀏覽器或是 node.js,在全域環境中都找不到 value 的值,故都會印出 undefined。
範例 1:
const obj = {
value: 1,
hello: function() {
console.log(this.value)
},
inner: {
value: 2,
hello: function() {
console.log(this.value)
}
}
}
const obj2 = obj.inner
const hello = obj.inner.hello
obj.inner.hello()
// obj.inner.hello.call(obj.inner) -> 2
obj2.hello()
// obj2.hello.call(obj2) -> 2
hello()
// hello.call() -> undefined
範例 2:
function hello() {
console.log(this)
}
var a = { value: 1, hello }
var b = { value: 2, hello }
hello() // hello.call() => window(瀏覽器非嚴格模式)
a.hello() // a.hello.call(a) => a
b.hello.apply(a) => 直接用 apply,所以就是 a
不合群的箭頭函式
因為箭頭函式沒有 this,所以在箭頭函式內看到 this,直接把它當作外面宣告的 this 即可,箭頭函式內外的 this 是一樣的東西。
箭頭函式內的 this 跟如何呼叫無關,跟如何定義有關。
"use strict"
const obj = {
x: 1,
hello: function(){
// 這邊印出來的 this 是什麼,test 的 this 就是什麼
// 在宣告它的地方的 this 是什麼,test 的 this 就是什麼
console.log(this)
const test = () => {
console.log(this)
// 所以這邊的 this 就是上面宣告的 this
}
test()
}
}
obj.hello()
// {x:1, hello: [Function: hello]} 即 obj 本身
// 同上 obj 本身
const hello = obj.hello
hello()
// undefined
// undefined
總結
- 脫離物件的 this 基本上沒有任何意義
- 沒有意義的 this 會根據嚴格模式以及環境給一個預設值
- 嚴格模式底下預設就是 undefined,非嚴格模式在瀏覽器底下預設值是 window
- 可以用 call、apply 與 bind 改變 this 的值
- 要看 this,就看這個函式「怎麽」被呼叫
- 可以把
hello()
看成hello.call()
,a.b.c.hello()
看成a.b.c.hello.call(a.b.c)
,以此類推,就能輕鬆找出 this 的值 - this 就是 call 一個函式時,傳入的第一個參數
參考資料:
淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
this 的值到底是什么?一次说清楚