最新版JavaScript中的箭頭函數(shù)
前言
本文可以讓你了解所有有關(guān)JavaScript箭頭函數(shù)的信息。我們將告訴你如何使用ES6的箭頭語(yǔ)法,以及在代碼中使用箭頭函數(shù)時(shí)需要注意的一些常見(jiàn)錯(cuò)誤。你會(huì)看到很多例子來(lái)說(shuō)明它們是如何工作的。
JavaScript的箭頭函數(shù)隨著ECMAScript 2015的發(fā)布而到來(lái),也被稱(chēng)為ES6。由于其簡(jiǎn)潔的語(yǔ)法和對(duì)this關(guān)鍵字的處理,箭頭函數(shù)迅速成為開(kāi)發(fā)者們最喜愛(ài)的功能。
箭頭函數(shù)語(yǔ)法
函數(shù)就像食譜一樣,你在其中存儲(chǔ)有用的指令,以完成你需要在程序中發(fā)生的事情,比如執(zhí)行一個(gè)動(dòng)作或返回一個(gè)值。通過(guò)調(diào)用函數(shù),來(lái)執(zhí)行食譜中包含的步驟。你可以在每次調(diào)用該函數(shù)時(shí)都這樣做,而不需要一次又一次地重寫(xiě)菜譜。
下面是在JavaScript中聲明函數(shù)并調(diào)用它的標(biāo)準(zhǔn)方法:
// function declaration function sayHiStranger() { return 'Hi, stranger!' } // call the function sayHiStranger()
你也可以編寫(xiě)同樣的函數(shù)作為函數(shù)表達(dá)式,就行這樣:
const sayHiStranger = function () { return 'Hi, stranger!' }
JavaScript箭頭函數(shù)始終是表達(dá)式。下面是如何使用箭頭符號(hào)重寫(xiě)上面的函數(shù):
const sayHiStranger = () => 'Hi, stranger'
這樣做的好處包括:
- 代碼只有一行
- 沒(méi)有
function
關(guān)鍵字 - 沒(méi)有
return
關(guān)鍵字 - 沒(méi)有大括號(hào)
{}
在JavaScript中,函數(shù)是一等公民。你可以把函數(shù)存儲(chǔ)在變量中,把它們作為參數(shù)傳遞給其他函數(shù),并從其他函數(shù)中把它們作為值返回。你可以使用JavaScript箭頭函數(shù)來(lái)做所有這些事情。
無(wú)圓括號(hào)語(yǔ)法
在上述示例中,函數(shù)是沒(méi)有參數(shù)的。在本例中,你必須在胖箭頭符號(hào)(=>
)之前添加一對(duì)空的圓括號(hào)()
。當(dāng)有多個(gè)參數(shù)時(shí)同理:
const getNetflixSeries = (seriesName, releaseDate) => `The ${seriesName} series was released in ${releaseDate}` // call the function console.log(getNetflixSeries('Bridgerton', '2020') ) // output: The Bridgerton series was released in 2020
如果只有一個(gè)參數(shù),你可以省略圓括號(hào)(你不必如此,但你可以這么做):
const favoriteSeries = seriesName => seriesName === "Bridgerton" ? "Let's watch it" : "Let's go out" // call the function console.log(favoriteSeries("Bridgerton")) // output: "Let's watch it"
當(dāng)你這么做的時(shí)候要小心一點(diǎn)。比如說(shuō),你決定使用默認(rèn)參數(shù),你必須將其包裹在圓括號(hào)中:
// with parentheses: correct const bestNetflixSeries = (seriesName = "Bridgerton") => `${seriesName} is the best` // outputs: "Bridgerton is the best" console.log(bestNetflixSeries()) // no parentheses: error const bestNetflixSeries = seriesName = "Bridgerton" => `${seriesName} is the best` // Uncaught SyntaxError: invalid arrow-function arguments (parentheses around the arrow-function may help)
隱式返回
在函數(shù)體內(nèi)只有一個(gè)表達(dá)式時(shí),你可以讓ES6的箭頭語(yǔ)法更加簡(jiǎn)潔。你可以把所有內(nèi)容放在一行,去掉大括號(hào),并移除return
關(guān)鍵字。
你已經(jīng)在上面的示例中看到了這些漂亮的一行代碼是如何工作的。下面的orderByLikes()
函數(shù)返回奈飛劇集對(duì)象的數(shù)組,按照最高點(diǎn)贊數(shù)排序:
// using the JS sort() function to sort the titles in descending order // according to the number of likes (more likes at the top, fewer at the bottom const orderByLikes = netflixSeries.sort((a, b) => b.likes - a.likes) // call the function // output:the titles and the n. of likes in descending order console.log(orderByLikes)
這種寫(xiě)法很酷,但是要注意代碼的可讀性。特別是在使用單行和無(wú)括號(hào)的ES6箭頭語(yǔ)法對(duì)一堆箭頭函數(shù)進(jìn)行排序時(shí)。就像這個(gè)例子:
const greeter = greeting => name => `${greeting}, ${name}!`
那里發(fā)生了什么?嘗試使用常規(guī)的函數(shù)語(yǔ)法:
function greeter(greeting) { return function(name) { return `${greeting}, ${name}!` } }
現(xiàn)在,你可以快速看到外部函數(shù)greeter
如何具有參數(shù)greeting
,并返回一個(gè)匿名函數(shù)。這個(gè)內(nèi)部函數(shù)又有一個(gè)叫做name
的參數(shù),并使用greeting
和name
的值返回一個(gè)字符串。下面是調(diào)用函數(shù)的方式:
const myGreet = greeter('Good morning') console.log( myGreet('Mary') ) // output: "Good morning, Mary!"
注意隱式返回錯(cuò)誤
當(dāng)你的JavaScript箭頭函數(shù)包含不止一個(gè)語(yǔ)句,你需要在大括號(hào)內(nèi)包裹所有語(yǔ)句,并使用return
關(guān)鍵字。
在下面的代碼中,該函數(shù)建立了一個(gè)包含幾個(gè)Netflix劇集的標(biāo)題和摘要的對(duì)象:
const seriesList = netflixSeries.map( series => { const container = {} container.title = series.name container.summary = series.summary // explicit return return container } )
.map()
函數(shù)中的箭頭函數(shù)在一系列的語(yǔ)句中展開(kāi),在語(yǔ)句的最后返回一個(gè)對(duì)象。這使得在函數(shù)主體周?chē)褂么罄ㄌ?hào)是不可避免的。
另外,由于正在使用花括號(hào),隱式返回便不是一個(gè)選項(xiàng)。你必須顯式使用return
關(guān)鍵字。
如果你的函數(shù)使用隱式返回來(lái)返回一個(gè)對(duì)象字面量,你需要使用圓括號(hào)來(lái)包裹該對(duì)象字面量。不這樣做將導(dǎo)致錯(cuò)誤,因?yàn)镴avaScript引擎將對(duì)象字面量的大括號(hào)錯(cuò)誤地解析為函數(shù)的大括號(hào)。正如你剛才注意到的,當(dāng)你在一個(gè)箭頭函數(shù)中使用大括號(hào)時(shí),你不能省略return
關(guān)鍵字。
前面代碼的較短版本演示了這種語(yǔ)法:
// Uncaught SyntaxError: unexpected token: ':' const seriesList = netflixSeries.map(series => { title: series.name }); // Works fine const seriesList = netflixSeries.map(series => ({ title: series.name }));
無(wú)法命名箭頭函數(shù)
在function
關(guān)鍵字和參數(shù)列表之間沒(méi)有名稱(chēng)標(biāo)識(shí)的函數(shù)被稱(chēng)為匿名函數(shù)。下面是常規(guī)匿名函數(shù)表達(dá)式的樣子:
const anonymous = function() { return 'You can\'t identify me!' }
箭頭函數(shù)都是匿名函數(shù):
const anonymousArrowFunc = () => 'You can\'t identify me!'
從ES6開(kāi)始,變量和方法可以通過(guò)匿名函數(shù)的語(yǔ)法位置,使用name
屬性來(lái)推斷其名稱(chēng)。這使得在檢查函數(shù)值或報(bào)告錯(cuò)誤時(shí)有可能識(shí)別該函數(shù)。
使用anonymousArrowFunc
檢查一下:
console.log(anonymousArrowFunc.name) // output: "anonymousArrowFunc"
需要注意的是,只有當(dāng)匿名函數(shù)被分配給一個(gè)變量時(shí),這個(gè)可以推斷的name
屬性才會(huì)存在,正如上面的例子。如果你使用匿名函數(shù)作為回調(diào)函數(shù),你就會(huì)失去這個(gè)有用的功能。在下面的演示中,.setInterval()
方法中的匿名函數(shù)無(wú)法利用name
屬性:
let counter = 5 let countDown = setInterval(() => { console.log(counter) counter-- if (counter === 0) { console.log("I have no name!!") clearInterval(countDown) } }, 1000)
這還不是全部。這個(gè)推斷的name
屬性仍然不能作為一個(gè)適當(dāng)?shù)臉?biāo)識(shí)符,你可以用它來(lái)指代函數(shù)本身--比如遞歸、解除綁定事件等。
如何處理this關(guān)鍵字
關(guān)于箭頭函數(shù),最重要的一點(diǎn)是它們處理this
關(guān)鍵字的方式。特別是,箭頭函數(shù)內(nèi)的this
關(guān)鍵字不會(huì)重新綁定。
為了說(shuō)明這意味著什么,請(qǐng)查看下面的演示。
這里有一個(gè)按鈕。點(diǎn)擊按鈕會(huì)觸發(fā)一個(gè)從5到1的反向計(jì)數(shù)器,它顯示在按鈕本身。
<button class="start-btn">Start Counter</button> ... const startBtn = document.querySelector(".start-btn"); startBtn.addEventListener('click', function() { this.classList.add('counting') let counter = 5; const timer = setInterval(() => { this.textContent = counter counter -- if(counter < 0) { this.textContent = 'THE END!' this.classList.remove('counting') clearInterval(timer) } }, 1000) })
注意到.addEventListener()
方法里面的事件處理器是一個(gè)常規(guī)的匿名函數(shù)表達(dá)式,而不是一個(gè)箭頭函數(shù)。為什么呢?如果在函數(shù)內(nèi)部打印this
的值,你會(huì)看到它引用了監(jiān)聽(tīng)器所連接的按鈕元素,這正是我們所期望的,也是程序按計(jì)劃工作所需要的:
startBtn.addEventListener('click', function() { console.log(this) ... })
下面是它在Firefox開(kāi)發(fā)人員工具控制臺(tái)中的樣子:
然后,嘗試使用箭頭函數(shù)來(lái)替代常規(guī)函數(shù),就像這樣:
startBtn.addEventListener('click', () => { console.log(this) ... })
現(xiàn)在,this
不再引用按鈕元素。相反,它引用Window
對(duì)象:
這意味著,如果你想要在按鈕被點(diǎn)擊之后,使用this
來(lái)為按鈕添加class
,你的代碼就無(wú)法正常工作:
// change button's border's appearance this.classList.add('counting')
下面是控制臺(tái)中的錯(cuò)誤信息:
當(dāng)你在JavaScript中使用箭頭函數(shù),this
關(guān)鍵字的值不會(huì)被重新綁定。它繼承自父作用域(也稱(chēng)為詞法作用域)。在這種特殊情況下,箭頭函數(shù)被作為參數(shù)傳遞給startBtn.addEventListener()
方法,該方法位于全局作用域中。因此,函數(shù)處理器中的this
也被綁定到全局作用域中--也就是Window
對(duì)象。
因此,如果你想讓this
引用程序中的開(kāi)始按鈕,正確的做法是使用一個(gè)常規(guī)函數(shù),而不是一個(gè)箭頭函數(shù)。
匿名箭頭函數(shù)
在上面的演示中,接下來(lái)要注意的是.setInterval()
方法中的代碼。在這里,你也會(huì)發(fā)現(xiàn)一個(gè)匿名函數(shù),但這次是一個(gè)箭頭函數(shù)。為什么?
請(qǐng)注意,如果你使用常規(guī)函數(shù),this
值會(huì)是多少:
const timer = setInterval(function() { console.log(this) ... }, 1000)
是button
元素嗎?并不是。這個(gè)值將會(huì)是Window
對(duì)象!
事實(shí)上,上下文已經(jīng)發(fā)生了變化,因?yàn)楝F(xiàn)在this
在一個(gè)非綁定的或全局的函數(shù)中,它被作為參數(shù)傳遞給.setInterval()
。因此,this
關(guān)鍵字的值也發(fā)生了變化,因?yàn)樗F(xiàn)在被綁定到全局作用域。
在這種情況下,一個(gè)常見(jiàn)的hack手段是包括另一個(gè)變量來(lái)存儲(chǔ)this
關(guān)鍵字的值,這樣它就會(huì)一直指向預(yù)期的元素--在這種情況下,就是button
元素:
const that = this const timer = setInterval(function() { console.log(that) ... }, 1000)
你也可以使用.bind()
來(lái)解決這個(gè)問(wèn)題:
const timer = setInterval(function() { console.log(this) ... }.bind(this), 1000)
有了箭頭函數(shù),問(wèn)題就徹底消失了。下面是使用箭頭函數(shù)時(shí)this
的值:
const timer = setInterval( () => { console.log(this) ... }, 1000)
這次,控制臺(tái)打印了button
,這就是我們想要的。事實(shí)上,程序要改變按鈕的文本,所以它需要this
來(lái)指代button
元素:
const timer = setInterval( () => { console.log(this) // the button's text displays the timer value this.textContent = counter }, 1000)
箭頭函數(shù)沒(méi)有自己的this
上下文。它們從父級(jí)繼承this
的值,正是因?yàn)檫@個(gè)特點(diǎn),在上面這種情況下就是很好的選擇。
不正常工作的情況
箭頭函數(shù)并不只是在JavaScript中編寫(xiě)函數(shù)的一種花里胡哨的新方法。它們有自己的局限性,這意味著在有些情況下你不想使用箭頭函數(shù)。讓我們看看更多的例子。
箭頭函數(shù)作為對(duì)象方法
箭頭函數(shù)作為對(duì)象上的方法不能很好地工作。
考慮這個(gè)netflixSeries
對(duì)象,上面有一些屬性和一系列方法。調(diào)用console.log(netflixSeries.getLikes())
應(yīng)該會(huì)打印一條信息,說(shuō)明當(dāng)前喜歡的人數(shù)。console.log(netflixSeries.addLike())
應(yīng)該會(huì)增加一個(gè)喜歡的人數(shù),然后在控制臺(tái)上打印新值:
const netflixSeries = { title: 'After Life', firstRealease: 2019, likes: 5, getLikes: () => `${this.title} has ${this.likes} likes`, addLike: () => { this.likes++ return `Thank you for liking ${this.title}, which now has ${this.likes} likes` } }
相反,調(diào)用.getLikes()
方法返回'undefined has NaN likes'
,調(diào)用.addLike()
方法返回'Thank you for liking undefined, which now has NaN likes'
。因此,this.title
和this.likes
未能分別引用對(duì)象的屬性title
和likes
。
這次,問(wèn)題出在箭頭函數(shù)的詞法作用域上。對(duì)象方法中的this
引用的是父對(duì)象的范圍,在本例中是Window
對(duì)象,而不是父對(duì)象本身--也就是說(shuō),不是netflixSeries
對(duì)象。
當(dāng)然,解決辦法是使用常規(guī)函數(shù):
const netflixSeries = { title: 'After Life', firstRealease: 2019, likes: 5, getLikes() { return `${this.title} has ${this.likes} likes` }, addLike() { this.likes++ return `Thank you for liking ${this.title}, which now has ${this.likes} likes` } } // call the methods console.log(netflixSeries.getLikes()) console.log(netflixSeries.addLike()) // output: After Life has 5 likes Thank you for liking After Life, which now has 6 likes
箭頭函數(shù)與第三方庫(kù)
另一個(gè)需要注意的問(wèn)題是,第三方庫(kù)通常會(huì)綁定方法調(diào)用,因此this
值會(huì)指向一些有用的東西。
比如說(shuō),在Jquery事件處理器內(nèi)部,this
將使你能夠訪問(wèn)處理器所綁定的DOM元素:
$('body').on('click', function() { console.log(this) }) // <body>
但是如果我們使用箭頭函數(shù),正如我們所看到的,它沒(méi)有自己的this
上下文,我們會(huì)得到意想不到的結(jié)果:
$('body').on('click', () =>{ console.log(this) }) // Window
下面是使用Vue的其他例子:
new Vue({ el: app, data: { message: 'Hello, World!' }, created: function() { console.log(this.message); } }) // Hello, World!
在created
鉤子內(nèi)部,this
被綁定到Vue實(shí)例上,因此會(huì)顯示'Hello, World!'
信息。
然而如果我們使用箭頭函數(shù),this
將會(huì)指向父作用域,上面沒(méi)有message
屬性:
new Vue({ el: app, data: { message: 'Hello, World!' }, created: () => { console.log(this.message); } }) // undefined
箭頭函數(shù)沒(méi)有arguments
對(duì)象
有時(shí),你可能需要?jiǎng)?chuàng)建一個(gè)具有無(wú)限參數(shù)個(gè)數(shù)的函數(shù)。比如,假設(shè)你想創(chuàng)建一個(gè)函數(shù),列出你最喜歡的奈飛劇集,并按照偏好排序。然而,你還不知道你要包括多少個(gè)劇集。JavaScript提供了arguments
對(duì)象。這是一個(gè)類(lèi)數(shù)組對(duì)象(不是完整的數(shù)組),在調(diào)用時(shí)存儲(chǔ)傳遞給函數(shù)的值。
嘗試使用箭頭函數(shù)實(shí)現(xiàn)此功能:
const listYourFavNetflixSeries = () => { // we need to turn the arguments into a real array // so we can use .map() const favSeries = Array.from(arguments) return favSeries.map( (series, i) => { return `${series} is my #${i +1} favorite Netflix series` } ) console.log(arguments) } console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life'))
當(dāng)你調(diào)用該函數(shù)時(shí),你會(huì)得到以下錯(cuò)誤:Uncaught ReferenceError: arguments is not defined
。這意味著arguments
對(duì)象在箭頭函數(shù)中是不可用的。事實(shí)上,將箭頭函數(shù)替換成常規(guī)函數(shù)就可以了:
const listYourFavNetflixSeries = function() { const favSeries = Array.from(arguments) return favSeries.map( (series, i) => { return `${series} is my #${i +1} favorite Netflix series` } ) console.log(arguments) } console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life')) // output: ["Bridgerton is my #1 favorite Netflix series", "Ozark is my #2 favorite Netflix series", "After Life is my #3 favorite Netflix series"]
因此,如果你需要arguments
對(duì)象,你不能使用箭頭函數(shù)。
但如果你真的想用一個(gè)箭頭函數(shù)來(lái)復(fù)制同樣的功能呢?你可以使用ES6剩余參數(shù)(...
)。下面是你該如何重寫(xiě)你的函數(shù):
const listYourFavNetflixSeries = (...seriesList) => { return seriesList.map( (series, i) => { return `${series} is my #${i +1} favorite Netflix series` } ) }
總結(jié)
通過(guò)使用箭頭函數(shù),你可以編寫(xiě)帶有隱式返回的單行代碼,以解決JavaScript中this
關(guān)鍵字的綁定問(wèn)題。箭頭函數(shù)在數(shù)組方法中也很好用,如.map()
、.sort()
、.forEach()
、.filter()
、和.reduce()
。但請(qǐng)記?。杭^函數(shù)并不能取代常規(guī)的JavaScript函數(shù)。記住,只有當(dāng)箭形函數(shù)是正確的工具時(shí),才能使用它。
javascript中的箭頭函數(shù)
1.箭頭函數(shù)相當(dāng)于匿名函數(shù),并且簡(jiǎn)化了函數(shù)定義。箭頭函數(shù)有兩種格式,一種像上面的,只包含一個(gè)表達(dá)式,連{ … }和return都省略掉了。還有一種可以包含多條語(yǔ)句,這時(shí)候就不能省略{ … }和return:
2. 箭頭=>前面是形參,箭頭后面是函數(shù)體,函數(shù)匿名
如果參數(shù)不是一個(gè),就需要用括號(hào)()括起來(lái):
如果要返回一個(gè)對(duì)象,就要注意,如果是單表達(dá)式,這么寫(xiě)的話會(huì)報(bào)錯(cuò):
x => { foo: x }應(yīng)該改為
x => ({ foo: x })
3.(this函數(shù)) 箭頭函數(shù)看上去是匿名函數(shù)的一種簡(jiǎn)寫(xiě),但實(shí)際上,箭頭函數(shù)和匿名函數(shù)有個(gè)明顯的區(qū)別:箭頭函數(shù)內(nèi)部的this是詞法作用域,由上下文確定
現(xiàn)在,箭頭函數(shù)完全修復(fù)了this的指向,this總是指向詞法作用域,也就是外層調(diào)用者obj。
到此這篇關(guān)于JavaScript中的箭頭函數(shù)的文章就介紹到這了,更多相關(guān)js箭頭函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過(guò)修改360搶票的刷新頻率和突破8車(chē)次限制實(shí)現(xiàn)方法
這篇文章主要介紹了通過(guò)修改360搶票的刷新頻率和突破8車(chē)次限制實(shí)現(xiàn)方法的相關(guān)資料,現(xiàn)在刷票工具很多,這里就舉一例修改,增加搶票頻率及突破8車(chē)次限制,需要的朋友可以參考下2017-01-01JS組件系列之使用HTML標(biāo)簽的data屬性初始化JS組件
這篇文章主要介紹了JS組件系列之使用HTML標(biāo)簽的data屬性初始化JS組件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09跟我學(xué)習(xí)javascript的執(zhí)行上下文
跟我學(xué)習(xí)javascript的執(zhí)行上下文,讀完本文后,你應(yīng)該清楚了解釋器做了什么,為什么函數(shù)和變量能在聲明前使用以及它們的值是如何決定的,需要了解這些內(nèi)容的朋友可以參考下2015-11-11Layui實(shí)現(xiàn)數(shù)據(jù)表格默認(rèn)全部顯示(不要分頁(yè))
今天小編就為大家分享一篇Layui實(shí)現(xiàn)數(shù)據(jù)表格默認(rèn)全部顯示(不要分頁(yè)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-10-10js實(shí)時(shí)監(jiān)聽(tīng)文本框狀態(tài)的方法
js實(shí)時(shí)監(jiān)聽(tīng)文本框狀態(tài)的方法,這里的方法兼容性不是很好,建議參考腳本之家以前的文章。2011-04-04