JS 中 this 關鍵詞的使用解析
在 JavaScript 中,this是一個極具辨識度又容易讓人混淆的關鍵詞。它的指向並非固定不變,而是取決於函數的調用方式,這一特性讓它在不同場景下表現出截然不同的行為。掌握this的綁定規則,是寫出健壯 JS 代碼的關鍵,尤其是在面向對象編程、事件處理和異步操作中,this的正確運用直接影響代碼邏輯的正確性。
一、全局作用域中的 this
在全局作用域下,this直接指向全局對象。瀏覽器環境中全局對象是window,Node.js 環境中則是global。
// 瀏覽器環境下測試
console.log(this === window); // 輸出:true
this.globalVar = "我是全局變量";
console.log(window.globalVar); // 輸出:我是全局變量
console.log(globalVar); // 輸出:我是全局變量(全局變量掛載在window上)
需要注意的是,在嚴格模式('use strict')下,全局作用域的this依然指向全局對象,但函數內的this會有不同表現。
二、函數調用中的 this
函數的調用方式是決定this指向的核心因素,不同調用方式對應不同的綁定規則。
1. 普通函數調用:this 指向全局對象
當函數以普通方式調用時,this默認綁定到全局對象。嚴格模式下,這種調用方式的this會變成undefined。
function normalFunc() {
console.log(this);
}
function strictFunc() {
'use strict';
console.log(this);
}
normalFunc(); // 瀏覽器中輸出window,Node中輸出global
strictFunc(); // 輸出undefined
2. 對象方法調用:this 指向調用對象
當函數作為對象的方法被調用時,this會隱式綁定到調用這個方法的對象上。
const user = {
name: "張三",
age: 28,
sayHello: function() {
console.log(`我是${this.name},今年${this.age}歲`);
}
};
user.sayHello(); // 輸出:我是張三,今年28歲
// 方法賦值給變量後,變成普通函數調用
const func = user.sayHello;
func(); // 瀏覽器中輸出:我是undefined,今年undefined歲(this指向window)
3. 構造函數調用:this 指向實例對象
當函數通過new關鍵字作為構造函數調用時,this會綁定到新創建的實例對象上。
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`Hi, ${this.name}`);
};
}
const person1 = new Person("李四", 30);
person1.sayHi(); // 輸出:Hi, 李四
console.log(person1.name); // 輸出:李四
構造函數的核心就是利用this給實例對象掛載屬性和方法。
4. 箭頭函數調用:this 繼承自外層作用域
箭頭函數沒有自己的this綁定,它的this繼承自外層最近的非箭頭函數的作用域。這一特性完美解決了傳統函數在異步回調、事件處理中的this指向問題。
const obj = {
name: "箭頭函數測試",
func: function() {
// 普通函數的this指向obj
setTimeout(() => {
// 箭頭函數繼承外層func的this
console.log(this.name);
}, 100);
},
badFunc: function() {
setTimeout(function() {
// 普通函數this指向window
console.log(this.name);
}, 100);
}
};
obj.func(); // 輸出:箭頭函數測試
obj.badFunc(); // 瀏覽器中輸出:空字符串(window.name默認為空)
注意:箭頭函數不能作為構造函數,使用new調用會報錯。
三、手動改變 this 指向的方法
JS 提供了三個內置方法,可以手動指定函數的this指向,分別是call()、apply()和bind()。
1. call ():參數逐個傳入
call()方法會立即執行函數,第一個參數是this的指向,後續參數是函數的入參,逐個傳入。
function calculate(a, b) {
return this.base + a + b;
}
const context = { base: 10 };
// this指向context,參數a=2,b=3
const result = calculate.call(context, 2, 3);
console.log(result); // 輸出:15
2. apply ():參數以數組形式傳入
apply()和call()功能一致,區別在於函數參數需要以數組的形式傳入。
// 複用上面的calculate函數
const result2 = calculate.apply(context, [4, 5]);
console.log(result2); // 輸出:19
3. bind ():返回綁定 this 的新函數
bind()不會立即執行函數,而是返回一個永久綁定了 this 指向的新函數,後續調用新函數時,this 不會改變。
// 複用上面的calculate函數
const boundFunc = calculate.bind(context);
// 調用新函數,參數逐個傳入
console.log(boundFunc(6, 7)); // 輸出:23
// bind可以預設部分參數
const partialFunc = calculate.bind(context, 8);
console.log(partialFunc(9)); // 輸出:27
四、實戰中的 this 常見坑點與解決
- 事件處理函數中的 this
瀏覽器事件處理函數中,this默認指向觸發事件的 DOM 元素,但在嵌套函數中容易丟失。
const btn = document.getElementById("test-btn");
btn.addEventListener("click", function() {
console.log(this); // 指向btn元素
setTimeout(function() {
console.log(this); // 指向window
}, 100);
});
// 解決方法:使用箭頭函數
btn.addEventListener("click", function() {
setTimeout(() => {
console.log(this); // 指向btn元素(繼承外層函數的this)
}, 100);
});
- 類方法中的 this 丟失
類的實例方法作為回調函數時,this 會丟失,需要手動綁定。
class Test {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
const test = new Test("類方法測試");
const print = test.printName;
print(); // 輸出:undefined(this指向window)
// 解決方法:使用bind綁定this
const boundPrint = test.printName.bind(test);
boundPrint(); // 輸出:類方法測試
this的指向規則看似複雜,實則有跡可循。記住調用方式決定 this 指向這一核心原則,再結合不同場景下的綁定規則和手動改變指向的方法,就能輕鬆駕馭這個 JS 中的 “靈魂” 關鍵詞。