今天我想來點 this
!
當時在學習 this
概念時,覺得有點複雜,但他又是 JavaScript 中滿重要的概念之一,
而且之前在看 JavaScript 相關面試題目 - javascript-questions 也發現 this
是 JavaScript 面試經典題,最近剛好在整理筆記時,想透過本篇文章再次整理一次,讓自己能輕易掌握概念。
this
是什麼?
在 JavaScript 中,this
的值是動態的,this
跟怎麼被呼叫有關,與放在哪無關!
來透過以下幾種情境與例子來看看 What is this
?
情境一:無明確的呼叫者時,
this
指向全局物件 window / global
在瀏覽器中,當你在全域作用域(也就是没有在任何函式内部)使用this
時,
他通常會指向全域物件window
。1
console.log(this); // 在瀏覽器中運行,通常輸出全域物件 window
在全域作用域中使用
console.log(this)
,會輸出全域物件window
。
因為在瀏覽器環境中,全域作用域下的程式碼就是在全域物件window
下執行的。在全域作用域中,如果沒有明確的呼叫者,
this
會默認指向全域物件~!
情境二:誰呼叫,誰就是 this
指在 JavaScript 中,this
的值取決於函式被誰呼叫。
宣告一個常數person
,其中包含函式sayHello
,
當這個函式被呼叫時,this
將指向呼叫他的對象,也就是person
。1
2
3
4
5
6
7
8const person = {
name: "Viii",
sayHello: function () {
console.log(`Hello,${this.name}!`);
},
};
person.sayHello(); // Hello, Viii!誰呼叫
sayHello
,this
就指向谁,這裡就會輸出 Hello, Viii!
情境三:當使用
new
關鍵字時,this
指向新建立的物件{}
1
2
3
4
5
6
7
8
9
10
11
12
13// 定義一個名為 Person 的建構函式
function Person(name, age) {
// 在建構函式中使用 this 來設定物件的屬性
this.name = name;
this.age = age;
}
// 使用 new 關鍵字來創建一個新的 Person 物件
const person1 = new Person('Viii', 18);
// 現在 person1 是一個新的物件,this 指向 person1
console.log(person1.name); // 輸出 "Viii"
console.log(person1.age); // 輸出 18我們定義了一個名為
Person
的建構函式,然後使用new
關鍵字來創建一個新的Person
物件person1
。當我們在建構函式中使用this
設定name
和age
屬性時,this
正確地指向了新建立的person1
物件。因此,person1.name
等於 Viii,person1.age
等於 18。使用
new
關鍵字時,this
指向新建立的物件{}
,這裡this
指向person1
情境四:Arrow Function (箭頭函式) 没有自己的
this
,會指向全域物件 window / globalArrow Function (箭頭函式) 在 JavaScript 中沒有自己的
this
綁定,而是捕獲了其所在上下文的this
值。也就是箭頭函式內部的this
值與包含他的函數或作用域的this
值是相同的,而不是在常規函數中,this
的值可以根據呼叫方式而變化。1
2
3
4
5
6
7
8
9
10
11
12// 在全局上下文中定義一個物件
const person = {
firstName: "Viii",
lastName: "Chang",
// 使用箭頭函數定義一個方法
getFullName: () => {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.getFullName()); // 輸出 "undefined undefined"在上面的例子中,
getFullName
方法是一個箭頭函數,但他嘗試訪問this.firstName
和this.lastName
,由於箭頭函數沒有自己的this
綁定,他會捕獲包含他的上下文,這裡是全域上下文。因此,他嘗試訪問全域上下文中的firstName
和lastName
,這些值在全域上下文中未定義,所以返回了 “undefined undefined”。相比之下,如果我們使用常規函數定義
getFullName
,它將能夠正確訪問person
物件的屬性:1
2
3
4
5
6
7
8
9
10
11const person = {
firstName: "Viii",
lastName: "Chang",
// 使用常規函數定義一個方法
getFullName: function() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.getFullName()); // 輸出 "Viii Chang"在這個例子中,
getFullName
方法是一個常規函數,它可以正確地訪問person
物件的屬性,因為它有自己的this
綁定,並且該綁定指向person
物件。
情境五:使用
.call()
.apply()
.bind()
以下分別說明這三種情境,明確指定函式內的
this
值:情境五之一:使用
.call()
call()
方法允許在呼叫函式時明確指定函式內的this
值,並且可以將參數傳遞給函式。
第一個參數是要指定的this
值,後續參數是傳遞給函式的引數。1
2
3
4
5
6
7
8
9function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = {
name: "Viii"
};
greet.call(person, "你好"); // 輸出 "你好, Viii!"call()
方法的第一個參數person
取代了greet
函式內的this
,所以在函式內部this.name
指向了person
物件的name
屬性。
情境五之二:使用
.apply()
apply()
方法與call()
方法類似,明確指定函式內的this
值,並且傳遞參數給函式。
不同之處在於,參數是以陣列形式傳遞的。1
2
3
4
5
6
7
8
9function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = {
name: "Bob"
};
greet.apply(person, ["嗨"]); // 輸出 "嗨, Bob!"apply()
方法的第一個參數person
取代了greet
函式內的this
,並且參數以陣列形式傳遞給函式。
情境五之三:使用
.bind()
bind()
方法不會立即呼叫函式,而是回傳一個新的函式,該函式內的this
值已經綁定到指定的值。這個新的函式可以稍後呼叫,它將始終具有綁定的this
值。1
2
3
4
5
6
7
8
9
10
11function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = {
name: "Charlie"
};
const greetPerson = greet.bind(person);
greetPerson("嘿"); // 輸出 "嘿, Charlie!"bind()
方法將greet
函式內的this
綁定到person
物件,並回傳一個新的函式greetPerson
。當呼叫greetPerson
時,仍然具有與person
物件相關聯的this
值。
情境六:是否有用 strict mode (嚴格模式)
自從 ECMAScript 5(ES5)中引入了 strict mode (嚴格模式),嚴格模式已經成為 JavaScript 中的一個重要特性,旨在提高代碼的安全性,發現並防止一些常見的錯誤。嚴格模式對於
this
的行為也產生了一些影響,特別是在以下情況:全域範圍中的
this
: 在非 “嚴格模式” 下,全域範圍中的this
通常指向全域物件(在瀏覽器中是window
)。但在 “嚴格模式” 下,全域範圍中的this
將是undefined
,而不是全域物件。這有助於防止意外地修改全域物件上的屬性。建構函式中的
this
: 在非 “嚴格模式” 下,如果在沒有使用new
關鍵字的情況下調用建構函式,this
會自動綁定到全域物件。但在 “嚴格模式” 下,如果未明確指定建構函式的執行上下文,this
會保持為undefined
,而不會自動綁定到全域物件。
用簡單的例子說明 “使用嚴格模式” 對
this
的影響:1
2
3
4
5
6
7;
function sayHello() {
console.log(this); // 在嚴格模式下,this 是 undefined
}
sayHello(); // 在嚴格模式下,不會將 this 綁定到全域物件
最後,想透過一個簡單的小題目,來複習一下今天的內容!
Q: How to make User0 do not return “undefined undefined”?
1
2
3
4
5
6
7
8
9
10
11
const User0 = {
firstName: "who",
lastName: "whowho",
getName: function () {
const hello = function () {
return `${this.firstName} ${this.lastName}`;
};
return hello();
},
};
console.log(User0.getName());
Solution 1: 使用
.call()
方法明確地將this
綁定到 User0 物件,並呼叫hello
函式。1
2
3
4
5
6
7
8
9
10
11const User0 = {
firstName: "Viii",
lastName: "Chang",
getName: function () {
const hello = function () {
return `${this.firstName} ${this.lastName}`;
};
return hello.call(this);
},
};
console.log(User0.getName());Solution 2: 使用
.apply()
方法明確地將this
綁定到 User0 物件,並呼叫hello
函式。1
2
3
4
5
6
7
8
9
10
11const User0 = {
firstName: "Viii",
lastName: "Chang",
getName: function () {
const hello = function () {
return `${this.firstName} ${this.lastName}`;
};
return hello.apply(this);
},
};
console.log(User0.getName());Solution 3: 使用
.bind()
方法將this
綁定到 User0 物件,然後立即呼叫hello
函式(IIFE - 立即調用的函式表達式)。1
2
3
4
5
6
7
8
9
10
11const User0 = {
firstName: "Viii",
lastName: "Chang",
getName: function () {
const hello = function () {
return `${this.firstName} ${this.lastName}`;
};
return hello.bind(this)();
},
};
console.log(User0.getName());Solution 4: 將
hello
函式改為箭頭函式,箭頭函式不會建立自己的 this 綁定,而是捕獲外部上下文的this
,因此可以正確地訪問 User0 物件的屬性。1
2
3
4
5
6
7
8
9
10
11const User0 = {
firstName: "Viii",
lastName: "Chang",
getName: function () {
const hello = () => {
return `${this.firstName} ${this.lastName}`;
};
return hello();
},
};
console.log(User0.getName());
呼~ this
真的是不簡單,以上是今天分享的內容,希望大家都能理解~!
我們下篇見!
參考資料:
➫ MDN - this
➫ 解釋 JavaScript 中 this 的值?
➫ JavaScript “this” 解釋與說明