停止濫用箭頭函數(shù):這五個(gè)場(chǎng)景請(qǐng)務(wù)必使用 function
自 ES6 問(wèn)世以來(lái),箭頭函數(shù)(Arrow Functions)以其簡(jiǎn)潔的語(yǔ)法和對(duì) this 的詞法綁定,迅速成為了 JavaScript 開(kāi)發(fā)者的“新寵”。我們似乎傾向于在任何可以使用函數(shù)的地方都換上 () => {}。
然而,箭頭函數(shù)并非“銀彈”,它并不能完全替代傳統(tǒng)的 function 關(guān)鍵字。過(guò)度濫用箭頭函數(shù),尤其是在不理解其工作原理的情況下,會(huì)導(dǎo)致難以追蹤的 bug 和意外行為。this 的指向是 JavaScript 中最核心也最容易混淆的概念之一,而箭頭函數(shù)和傳統(tǒng) function 在 this 的處理上有著本質(zhì)區(qū)別。
核心區(qū)別速記:
- function: this 的值是在函數(shù)被調(diào)用時(shí)動(dòng)態(tài)決定的,取決于誰(shuí)調(diào)用了它。
- => (箭頭函數(shù)): 它沒(méi)有自己的 this。它會(huì)捕獲其定義時(shí)所在上下文的 this 值,這個(gè)綁定是固定的,不會(huì)改變。
理解了這一點(diǎn),我們就會(huì)明白為什么在以下 5 個(gè)場(chǎng)景中,堅(jiān)持使用 function 不僅是最佳實(shí)踐,甚至是唯一的正確選擇。

場(chǎng)景一:對(duì)象的方法 (Object Methods)
這是最經(jīng)典、最常見(jiàn)的場(chǎng)景。當(dāng)我們?yōu)橐粋€(gè)對(duì)象定義方法時(shí),通常希望 this 指向該對(duì)象本身,以便訪(fǎng)問(wèn)其屬性。
? 錯(cuò)誤示范 (使用箭頭函數(shù)):
const person = {
name: '老王',
age: 30,
sayHi: () => {
// 這里的 this 繼承自全局作用域 (在瀏覽器中是 window),而不是 person 對(duì)象
console.log(`大家好,我是 ${this.name}`);
}
};
person.sayHi(); // 輸出: "大家好,我是 " (或者 "大家好,我是 undefined")在這個(gè)例子中,箭頭函數(shù) sayHi 在 person 對(duì)象中定義,但它的 this 捕獲的是定義 person 對(duì)象時(shí)的上下文,即全局作用域。全局作用域下沒(méi)有 name 屬性,所以結(jié)果不是我們想要的。
? 正確姿勢(shì) (使用 function):
const person = {
name: '老王',
age: 30,
sayHi: function() {
// 這里的 this 在調(diào)用時(shí)被動(dòng)態(tài)綁定為 person 對(duì)象
console.log(`大家好,我是 ${this.name}`);
},
// ES6 對(duì)象方法簡(jiǎn)寫(xiě)形式,本質(zhì)上也是一個(gè) function
sayHiShorthand() {
console.log(`大家好,我是 ${this.name}`);
}
};
person.sayHi(); // 輸出: "大家好,我是 老王"
person.sayHiShorthand(); // 輸出: "大家好,我是 老王"結(jié)論: 當(dāng)我們?yōu)閷?duì)象定義一個(gè)需要引用該對(duì)象自身屬性的方法時(shí),請(qǐng)使用 function 或 ES6 方法簡(jiǎn)寫(xiě)。
場(chǎng)景二:DOM 事件監(jiān)聽(tīng)器 (Event Listeners)
在使用 addEventListener 為 DOM 元素綁定事件時(shí),我們常常需要訪(fǎng)問(wèn)觸發(fā)該事件的元素本身(例如,修改它的樣式、內(nèi)容等)。傳統(tǒng) function 會(huì)自動(dòng)將 this 綁定到該 DOM 元素上。
? 錯(cuò)誤示范 (使用箭頭函數(shù)):
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
// 這里的 this 依然是 window 或 undefined,而不是 button 元素
this.classList.toggle('active'); // TypeError: Cannot read properties of undefined (reading 'classList')
});箭頭函數(shù)再次從外部作用域捕獲 this,導(dǎo)致我們無(wú)法直接操作點(diǎn)擊的按鈕。
? 正確姿勢(shì) (使用 function):
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
// 在這里,this 被正確地綁定為觸發(fā)事件的 button 元素
console.log(this); // <button id="myButton">...</button>
this.classList.toggle('active'); // 正常工作
});結(jié)論: 在 DOM 事件監(jiān)聽(tīng)回調(diào)中,如果我們需要用 this 來(lái)引用觸發(fā)事件的元素,請(qǐng)使用 function。
場(chǎng)景三:構(gòu)造函數(shù) (Constructor Functions)
箭頭函數(shù)在設(shè)計(jì)上就不能作為構(gòu)造函數(shù)使用。如果我們嘗試用 new 關(guān)鍵字來(lái)調(diào)用一個(gè)箭頭函數(shù),JavaScript 會(huì)直接拋出錯(cuò)誤。這是因?yàn)闃?gòu)造函數(shù)需要有自己的 this 來(lái)指向新創(chuàng)建的實(shí)例,并且需要一個(gè) prototype 屬性,而箭頭函數(shù)兩者都不具備。
? 錯(cuò)誤示范 (使用箭頭函數(shù)):

? 正確姿勢(shì) (使用 function 或 class):

結(jié)論: 永遠(yuǎn)不要用箭頭函數(shù)作為構(gòu)造函數(shù)。請(qǐng)使用 function 或 class。
場(chǎng)景四:原型方法 (Prototype Methods)
與對(duì)象方法類(lèi)似,當(dāng)我們?yōu)闃?gòu)造函數(shù)的原型 prototype 添加方法時(shí),我們也希望 this 指向調(diào)用該方法的實(shí)例。
? 錯(cuò)誤示范 (使用箭頭函數(shù)):

? 正確姿舍 (使用 function):

結(jié)論: 在 prototype 上定義方法時(shí),請(qǐng)使用 function,以確保 this 指向類(lèi)的實(shí)例。
場(chǎng)景五:需要 arguments 對(duì)象的函數(shù)
箭頭函數(shù)沒(méi)有自己的 arguments 對(duì)象。arguments 是一個(gè)類(lèi)數(shù)組對(duì)象,包含了函數(shù)被調(diào)用時(shí)傳入的所有參數(shù)。如果我們?cè)诩^函數(shù)內(nèi)部訪(fǎng)問(wèn) arguments,它只會(huì)訪(fǎng)問(wèn)到外層(如果存在)傳統(tǒng)函數(shù)的 arguments 對(duì)象。
? 錯(cuò)誤示范 (使用箭頭函數(shù)):

? 正確姿勢(shì) (使用 function):

注意: 在現(xiàn)代 JavaScript 中,更推薦使用剩余參數(shù) (...args) 來(lái)處理不確定數(shù)量的參數(shù)。剩余參數(shù)是真正的數(shù)組,并且它在箭頭函數(shù)和傳統(tǒng)函數(shù)中都能正常工作。但如果我們需要維護(hù)舊代碼,或者有特殊理由需要使用 arguments 對(duì)象,那么 function 是我們唯一的選擇。

那么,什么時(shí)候應(yīng)該用箭頭函數(shù)?
箭頭函數(shù)依然非常優(yōu)秀和極為有用,它的主要優(yōu)勢(shì)在于其詞法 this 綁定,完美解決了過(guò)去 var self = this 或 .bind(this) 的冗長(zhǎng)寫(xiě)法。
最佳使用場(chǎng)景:
回調(diào)函數(shù):尤其是在 map, filter, forEach 等數(shù)組方法中,或者在 setTimeout, Promise.then 內(nèi)部,當(dāng)我們需要保持外部 this 上下文時(shí)。
const timer = {
seconds: 0,
start() {
setInterval(() => {
// 這里的 this 正確地指向 timer 對(duì)象,因?yàn)榧^函數(shù)捕獲了 start 方法的 this
this.seconds++;
console.log(this.seconds);
}, 1000);
}
};
timer.start();





























