JS箭頭函式和常規函式之間的區別例項分析【 5 個區別】
本文例項講述了JS箭頭函式和常規函式之間的區別。分享給大家供大家參考,具體如下:
在 JavaScript 中,你可以通過多種方式去定義函式。
第一種常用的方法是使用關鍵字 function
:
// 函式宣告 function greet(who) { return `Hello,${who}!`; } // 函式表示式 const greet = function(who) { return `Hello,${who}`; }
程式碼中的函式宣告和函式表示式被稱為“常規函式”。
從 ES2015 開始,第二種可用的方法是 箭頭函式 語法:
const greet = (who) => { return `Hello,${who}!`; }
雖然兩者的語法都能夠定義函式,但是在開發時該怎麼選擇呢?這是個好問題。
在本文中,我將展示兩者之間的主要區別,以供你能夠根據需要選擇正確的語法。
1. this 值
1.1常規函式
在常規 JavaScript 函式內部,this
值(即執行上下文)是動態的。
動態上下文意味著 this
的值取決於如何呼叫函式。在 JavaScript 中,有 4 種呼叫常規函式的方式。
在簡單呼叫過程中,this
的值等於全域性物件(如果函式在嚴格模式下執行,則為 undefined
):
function myFunction() { console.log(this); } // 簡單呼叫 myFunction(); // logs global object (window)
在方法呼叫過程中,this
的值是擁有該方法的物件:
const myObject = { method() { console.log(this); } }; // 方法呼叫 myObject.method(); // logs "myObject"
在使用 myFunc.call(context,arg1,...,argN)
或 myFunc.apply(context,[arg1,argN])
的間接呼叫中,this
的值等於第一個引數:
function myFunction() { console.log(this); } const myContext = { value: 'A' }; myFunction.call(myContext); // logs { value: 'A' } myFunction.apply(myContext); // logs { value: 'A' }
在使用關鍵字 new
的建構函式呼叫期間,this
等於新建立的例項:
function MyFunction() { console.log(this); } new MyFunction(); // logs an instance of MyFunction
1.2箭頭函式
箭頭函式中 this
的行為與常規函式的 this
行為有很大不同。
無論如何執行或在何處執行,箭頭函式內部的 this
值始終等於外部函式的 this
值。換句話說,箭頭函式可按詞法解析 this
,箭頭函式沒有定義自己的執行上下文。
在以下示例中,myMethod()
是箭頭函式 callback()
的外部函式:
const myObject = { myMethod(items) { console.log(this); // logs "myObject" const callback = () => { console.log(this); // logs "myObject" }; items.forEach(callback); } }; myObject.myMethod([1,2,3]);
箭頭函式 callback()
中的 this
值等於外部函式 myMethod()
的 this
。
this
詞法解析是箭頭函式的重要功能之一。在方法內部使用回撥時,要確保箭頭函式沒有定義自己的 this
:不再有 const self = this
或者 callback.bind(this)
這種解決方法。
2.建構函式
2.1 常規函式
如上一節所述,常規函式可以輕鬆的構造物件。
例如用 Car()
函式建立汽車的例項:
function Car(color) { this.color = color; } const redCar = new Car('red'); redCar instanceof Car; // => true
Car
是常規函式,使用關鍵字 new
呼叫時會建立 Car
型別的新例項。
2.2 箭頭函式
this
詞法解決了箭頭函式不能用作建構函式。
如果你嘗試呼叫帶有 new
關鍵字字首的箭頭函式,則 JavaScript 會引發錯誤:
const Car = (color) => { this.color = color; }; const redCar = new Car('red'); // TypeError: Car is not a constructor
呼叫 new Car('red')
(其中 Car
是箭頭函式)會丟擲 TypeError: Car is not a constructor
。
3. arguments 物件
3.1 常規函式
在常規函式的主體內部,arguments
是一個特殊的類似於陣列的物件,其中包含被呼叫函式的引數列表。
讓我們用 3 個引數呼叫 myFunction
函式:
function myFunction() { console.log(arguments); } myFunction('a','b'); // logs { 0: 'a',1: 'b'}
類似於陣列物件的 arguments
中包含呼叫引數:'a'
和 'b'
。
3.2箭頭函式
另一方面,箭頭函式內部未定義 arguments
特殊關鍵字。
用詞法解析 arguments
物件:箭頭函式從外部函式訪問 arguments
。
讓我們試著在箭頭函式內部訪問 arguments
:
function myRegularFunction() { const myArrowFunction = () => { console.log(arguments); } myArrowFunction('c','d'); } myRegularFunction('a',1: 'b' }
箭頭函式 myArrowFunction()
由引數 'c'
,'d'
呼叫。在其主體內部,arguments
物件等於呼叫 myRegularFunction()
的引數: 'a'
,'b'
。
如果你想訪問箭頭函式的直接引數,可以使用剩餘引數 ...args
:
function myRegularFunction() { const myArrowFunction = (...args) => { console.log(args); } myArrowFunction('c','b'); // logs { 0: 'c',1: 'd' }
剩餘引數 ... args
接受箭頭函式的執行引數:{ 0: 'c',1: 'd' }
。
4.隱式返回
4.1常規函式
使用 return expression
語句從函式返回結果:
function myFunction() { return 42; } myFunction(); // => 42
如果缺少 return
語句,或者 return 語句後面沒有表示式,則常規函式隱式返回 undefined
:
function myEmptyFunction() { 42; } function myEmptyFunction2() { 42; return; } myEmptyFunction(); // => undefined myEmptyFunction2(); // => undefined
4.2箭頭函式
可以用與常規函式相同的方式從箭頭函式返回值,但有一個有用的例外。
如果箭頭函式包含一個表示式,而你省略了該函式的花括號,則將顯式返回該表示式。這些是內聯箭頭函式
const increment = (num) => num + 1; increment(41); // => 42
increment()
僅包含一個表示式:num + 1
。該表示式由箭頭函式隱式返回,而無需使用 return
關鍵字。
5. 方法
5.1 常規函式
常規函式是在類上定義方法的常用方式。
在下面 Hero
類中,用了常規函式定義方法 logName()
:
class Hero { constructor(heroName) { this.heroName = heroName; } logName() { console.log(this.heroName); }} const batman = new Hero('Batman');
通常把常規函式用作方法。
有時你需要把該方法作為回撥提供給 setTimeout()
或事件監聽器。在這種情況下,你可能會很難以訪問 this
的值。
例如用 logName()
方法作為 setTimeout()
的回撥:
setTimeout(batman.logName,1000); // after 1 second logs "undefined"
1 秒鐘後,undefined
會輸出到控制檯。 setTimeout()
執行 logName
的簡單呼叫(其中 this
是全域性物件)。這時方法會與物件分離。
讓我們手動把 this
值繫結到正確的上下文:
setTimeout(batman.logName.bind(batman),1000); // after 1 second logs "Batman"
batman.logName.bind(batman)
將 this
值繫結到 batman
例項。現在,你可以確定該方法不會丟失上下文。
手動繫結 this
需要樣板程式碼,尤其是在你有很多方法的情況下。有一種更好的方法:把箭頭函式作為類欄位。
5.2 箭頭函式
感謝類欄位提案(目前在第3階段),你可以將箭頭函式用作類中的方法。
與常規函式相反,現在用箭頭定義的方法能夠把 this
詞法繫結到類例項。
讓我們把箭頭函式作為欄位:
class Hero { constructor(heroName) { this.heroName = heroName; } logName = () => { console.log(this.heroName); } } const batman = new Hero('Batman');
現在,你可以把 batman.logName
用於回撥而無需手動繫結 this
。 logName()
方法中 this
的值始終是類例項:
setTimeout(batman.logName,1000); // after 1 second logs "Batman"
6. 總結
瞭解常規函式和箭頭函式之間的差異有助於為特定需求選擇正確的語法。
常規函式中的 this
值是動態的,並取決於呼叫方式。是箭頭函式中的 this
在詞法上是繫結的,等於外部函式的 this
。
常規函式中的 arguments
物件包含引數列表。相反,箭頭函式未定義 arguments
(但是你可以用剩餘引數 ...args
輕鬆訪問箭頭函式引數)。
如果箭頭函式有一個表示式,則即使不用 return
關鍵字也將隱式返回該表示式。
最後一點,你可以在類內部使用箭頭函式語法定義去方法。粗箭頭方法將 this
值繫結到類例項。
不管怎樣呼叫胖箭頭方法,this
始終等於類例項,在回撥這些方法用時非常有用。
感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。
更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》
希望本文所述對大家JavaScript程式設計有所幫助。