Gula sintaksis ES6 termasuk: 1. Objek literal, yang merujuk kepada objek yang dinyatakan secara langsung dalam bentuk "{}" 2. Fungsi anak panah, kaedah baharu untuk menulis fungsi tanpa nama; yang membolehkan Mengikut corak tertentu, ekstrak nilai dari tatasusunan atau objek dan tetapkan nilai kepada pembolehubah 4. Baki parameter dan pengembang 5. Rentetan templat 6. Pernyataan perisytiharan biarkan dan const;
Persekitaran pengendalian tutorial ini: sistem Windows 7, ECMAScript versi 6, komputer Dell G3.
Gula sintaksis: merujuk kepada sintaks dalam bahasa pengaturcaraan yang boleh menyatakan operasi dengan lebih mudah. ??Ia boleh memudahkan pengaturcara menggunakan bahasa tersebut dan operasi boleh menjadi lebih jelas, lebih mudah atau lebih konsisten dengan program tabiat pengaturcaraan.
ES6 menyediakan kemas kini yang tidak merosakkan untuk beberapa fungsi sedia ada. Kebanyakan kemas kini ini boleh difahami sebagai gula sintaksis, yang dipanggil gula sintaksis Ini bermakna sintaks baharu ini boleh melakukan Perkara sebenarnya boleh dilakukan dengan ES5. tetapi ia akan menjadi sedikit lebih rumit. Dalam bab ini, kami akan menumpukan pada gula sintaksis ini Selepas membaca ini, anda mungkin mempunyai pemahaman yang berbeza tentang beberapa sintaks ES6 baharu yang anda kenali.
Objek literal
Objek literal merujuk kepada objek yang dinyatakan secara langsung dalam bentuk {}
, seperti yang berikut:
var book = { title: 'Modular ES6', author: 'Nicolas', publisher: 'O′Reilly' }
ES6 membawa beberapa penambahbaikan pada sintaks literal objek: termasuk perwakilan ringkas sifat/kaedah, nama sifat boleh dikira, dll. Mari lihat satu persatu:
Perwakilan atribut yang ringkas
Pernahkah anda menghadapi senario ini yang kami isytiharkan mengandungi beberapa atribut, yang nilai atributnya diwakili oleh pembolehubah dan nama pembolehubah adalah sama dengan nama atribut? . Sebagai contoh, seperti yang ditunjukkan di bawah, kami ingin menetapkan tatasusunan bernama listeners
kepada atribut events
dalam objek listeners
Dengan menggunakan ES5 kami akan melakukan ini:
var listeners = [] function listen() {} var events = { listeners: listeners, listen: listen }
ES6 membolehkan kami untuk. singkatkannya sebagai Bagaimana pula dengan bentuk berikut:
var listeners = [] function listen() {} var events = { listeners, listen }
Bukankah ianya lebih ringkas Kaedah penulisan ringkas menggunakan literal objek membolehkan kita mengurangkan kod berulang tanpa menjejaskan semantik.
Ini adalah salah satu faedah ES6 Ia menyediakan banyak sintaks yang lebih ringkas dan jelas dari segi semantik, yang meningkatkan kebolehbacaan dan kebolehselenggaraan kod kami.
Nama sifat boleh dikira
Satu lagi kemas kini penting untuk literal objek adalah untuk membolehkan anda menggunakan nama sifat boleh dikira Dalam ES5 kami juga boleh menambah Nama atribut ialah atribut pembolehubah. Secara umumnya, kita perlu melakukan ini dengan cara berikut mula-mula mengisytiharkan pembolehubah bernama expertise
, dan kemudian menambah pembolehubah sebagai atribut objek person[expertise]
dalam bentuk person
:
var expertise = 'journalism' var person = { name: 'Sharon', age: 27 } person[expertise] = { years: 5, interests: ['international', 'politics', 'internet'] }
Dalam ES6, literal objek boleh menggunakan nama sifat yang dikira Letakkan sebarang ungkapan dalam kurungan persegi, dan hasil ungkapan itu akan menjadi nama sifat yang sepadan Kod di atas digunakan dalam ES6 Ia boleh ditulis seperti ini :
var expertise = 'journalism' var person = { name: 'Sharon', age: 27, [expertise]: { years: 5, interests: ['international', 'politics', 'internet'] } }
Walau bagaimanapun, perlu diambil perhatian bahawa atribut yang disingkatkan dan nama atribut yang dikira tidak boleh digunakan pada masa yang sama . Ini kerana sifat yang disingkatkan ialah gula sintaksis yang berkuat kuasa pada peringkat penyusunan, manakala nama sifat yang dikira berkuat kuasa pada masa jalan. Jika anda mencampurkan kedua-duanya, kod akan melaporkan ralat. Selain itu, mencampurkan kedua-duanya sering mengurangkan kebolehbacaan kod, jadi ia juga merupakan perkara yang baik untuk JavaScript untuk menyekat pencampuran kedua-duanya pada tahap bahasa.
var expertise = 'journalism' var journalism = { years: 5, interests: ['international', 'politics', 'internet'] } var person = { name: 'Sharon', age: 27, [expertise] // 這里會(huì)報(bào)語(yǔ)法錯(cuò)誤 }
Nama sifat yang boleh dikira akan menjadikan kod kami lebih ringkas apabila menghadapi situasi berikut:
Sifat objek baharu merujuk kepada Objek lain:
var grocery = { id: 'bananas', name: 'Bananas', units: 6, price: 10, currency: 'USD' } var groceries = { [grocery.id]: grocery }
Nama atribut objek yang akan dibina berasal daripada parameter fungsi. Jika kita menggunakan ES5 untuk menangani masalah ini, kita perlu mengisytiharkan objek literal dahulu, kemudian menambah atribut secara dinamik, dan kemudian mengembalikan objek tersebut. Dalam contoh berikut, kami mencipta fungsi yang bertindak balas kepada permintaan Ajax Fungsi fungsi ini ialah apabila permintaan gagal, objek yang dikembalikan mempunyai atribut bernama
error
dan huraian yang sepadan apabila permintaan itu berjaya nama Ia ialah atributsuccess
dan huraian yang sepadan.
// ES5 寫(xiě)法 function getEnvelope(type, description) { var envelope = { data: {} } envelope[type] = description return envelope }
Menggunakan nama harta terkira yang disediakan oleh ES6, pelaksanaan yang lebih ringkas adalah seperti berikut:
// ES6 寫(xiě)法 function getEnvelope(type, description) { return { data: {}, [type]: description } }
Sifat literal objek boleh disingkatkan, dan kaedahnya sebenarnya sama OK.
Takrifan kaedah
Mari kita lihat dahulu cara kaedah objek ditakrifkan secara tradisional Dalam kod berikut, kami membina penjana peristiwa yang menggunakan kaedah on
daftar acara, dan kaedah emit
digunakan untuk melaksanakan acara:
var emitter = { events: {}, on: function (type, fn) { if (this.events[type] === undefined) { this.events[type] = [] } this.events[type].push(fn) }, emit: function (type, event) { if (this.events[type] === undefined) { return } this.events[type].forEach(function (fn) { fn(event) }) } }
Singkatan kaedah literal objek ES6 membolehkan kami meninggalkan kata kunci function
dan kolon berikut bagi kaedah objek, dan ditulis semula kod Seperti berikut:
var emitter = { events: {}, on(type, fn) { if (this.events[type] === undefined) { this.events[type] = [] } this.events[type].push(fn) }, emit(type, event) { if (this.events[type] === undefined) { return } this.events[type].forEach(function (fn) { fn(event) }) } }
Fungsi anak panah dalam ES6 terkenal Ia mempunyai beberapa kelebihan istimewa (kira-kira this
Mungkin anda, seperti saya, telah lama menggunakan fungsi anak panah, tetapi terdapat beberapa butiran yang saya telah pelajari sebelum ini Tetapi saya tidak pernah memahaminya, seperti beberapa singkatan fungsi anak panah dan langkah berjaga-jaga untuk penggunaannya.
箭頭函數(shù)
JS中聲明的普通函數(shù),一般有函數(shù)名,一系列參數(shù)和函數(shù)體,如下:
function name(parameters) { // function body }
普通匿名函數(shù)則沒(méi)有函數(shù)名,匿名函數(shù)通常會(huì)被賦值給一個(gè)變量/屬性,有時(shí)候還會(huì)被直接調(diào)用:
var example = function (parameters) { // function body }
ES6 為我們提供了一種寫(xiě)匿名函數(shù)的新方法,即箭頭函數(shù)。箭頭函數(shù)不需要使用function
關(guān)鍵字,其參數(shù)和函數(shù)體之間以=>
相連接:
var example = (parameters) => { // function body }
盡管箭頭函數(shù)看起來(lái)類(lèi)似于傳統(tǒng)的匿名函數(shù),他們卻具有根本性的不同:
箭頭函數(shù)不能被直接命名,不過(guò)允許它們賦值給一個(gè)變量;
箭頭函數(shù)不能用做構(gòu)造函數(shù),你不能對(duì)箭頭函數(shù)使用
new
關(guān)鍵字;箭頭函數(shù)也沒(méi)有
prototype
屬性;箭頭函數(shù)綁定了詞法作用域,不會(huì)修改
this
的指向。
最后一點(diǎn)是箭頭函數(shù)最大的特點(diǎn),我們來(lái)仔細(xì)看看。
詞法作用域
我們?cè)诩^函數(shù)的函數(shù)體內(nèi)使用的this
,arguments
,super
等都指向包含箭頭函數(shù)的上下文,箭頭函數(shù)本身不產(chǎn)生新的上下文。下述代碼中,我們創(chuàng)建了一個(gè)名為timer
的對(duì)象,它的屬性seconds
用以計(jì)時(shí),方法start
用以開(kāi)始計(jì)時(shí),若我們?cè)谌舾擅牒笳{(diào)用start
方法,將打印出當(dāng)前的seconds
值。
// ES5 var timer = { seconds: 0, start() { setInterval(function(){ this.seconds++ }, 1000) } } timer.start() setTimeout(function () { console.log(timer.seconds) }, 3500) > 0
// ES6 var timer = { seconds: 0, start() { setInterval(() => { this.seconds++ }, 1000) } } timer.start() setTimeout(function () { console.log(timer.seconds) }, 3500) // <- 3
第一段代碼中start
方法使用的是常規(guī)的匿名函數(shù)定義,在調(diào)用時(shí)this
將指向了window
,console
出的結(jié)果為undefined
,想要讓代碼正常工作,我們需要在start
方法開(kāi)頭處插入var self = this
,然后替換匿名函數(shù)函數(shù)體中的this
為self
,第二段代碼中,我們使用了箭頭函數(shù),就不會(huì)發(fā)生這種情況了。
還需要說(shuō)明的是,箭頭函數(shù)的作用域也不能通過(guò).call
,.apply
,.bind
等語(yǔ)法來(lái)改變,這使得箭頭函數(shù)的上下文將永久不變。
我們?cè)賮?lái)看另外一個(gè)箭頭函數(shù)與普通匿名函數(shù)的不同之處,你猜猜,下面的代碼最終打印出的結(jié)果會(huì)是什么:
function puzzle() { return function () { console.log(arguments) } } puzzle('a', 'b', 'c')(1, 2, 3)
答案是1,2,3
,原因是對(duì)常規(guī)匿名函數(shù)而言,arguments
指向匿名函數(shù)本身。
作為對(duì)比,我們看看下面這個(gè)例子,再猜猜,打印結(jié)果會(huì)是什么?
function puzzle() { return ()=>{ console.log(arguments) } } puzzle('a', 'b', 'c')(1, 2, 3)
答案是a,b,c
,箭頭函數(shù)的特殊性決定其本身沒(méi)有arguments
對(duì)象,這里的arguments
其實(shí)是其父函數(shù)puzzle
的。
前面我們提到過(guò),箭頭函數(shù)還可以簡(jiǎn)寫(xiě),接下來(lái)我們一起看看。
簡(jiǎn)寫(xiě)的箭頭函數(shù)
完整的箭頭函數(shù)是這樣的:
var example = (parameters) => { // function body }
簡(jiǎn)寫(xiě)1:
當(dāng)只有一個(gè)參數(shù)時(shí),我們可以省略箭頭函數(shù)參數(shù)兩側(cè)的括號(hào):
var double = value => { return value * 2 }
簡(jiǎn)寫(xiě)2:
對(duì)只有單行表達(dá)式且,該表達(dá)式的值為返回值的箭頭函數(shù)來(lái)說(shuō),表征函數(shù)體的{}
,可以省略,return
關(guān)鍵字可以省略,會(huì)靜默返回該單一表達(dá)式的值。
var double = (value) => value * 2
簡(jiǎn)寫(xiě)3:
上述兩種形式可以合并使用,而得到更加簡(jiǎn)潔的形式
var double = value => value * 2
現(xiàn)在,你肯定學(xué)會(huì)了箭頭函數(shù)的基本使用方法,接下來(lái)我們?cè)倏磶讉€(gè)使用示例。
簡(jiǎn)寫(xiě)箭頭函數(shù)帶來(lái)的一些問(wèn)題
當(dāng)你的簡(jiǎn)寫(xiě)箭頭函數(shù)返回值為一個(gè)對(duì)象時(shí),你需要用小括號(hào)括起你想返回的對(duì)象。否則,瀏覽器會(huì)把對(duì)象的{}
解析為箭頭函數(shù)函數(shù)體的開(kāi)始和結(jié)束標(biāo)記。
// 正確的使用形式 var objectFactory = () => ({ modular: 'es6' })
下面的代碼會(huì)報(bào)錯(cuò),箭頭函數(shù)會(huì)把本想返回的對(duì)象的花括號(hào)解析為函數(shù)體,number
被解析為label
,value
解釋為沒(méi)有做任何事情表達(dá)式,我們又沒(méi)有顯式使用return
,返回值默認(rèn)是undefined
。
[1, 2, 3].map(value => { number: value }) // <- [undefined, undefined, undefined]
當(dāng)我們返回的對(duì)象字面量不止一個(gè)屬性時(shí),瀏覽器編譯器不能正確解析第二個(gè)屬性,這時(shí)會(huì)拋出語(yǔ)法錯(cuò)誤。
[1, 2, 3].map(value => { number: value, verified: true }) // <- SyntaxError
解決方案是把返回的對(duì)象字面量包裹在小括號(hào)中,以助于瀏覽器正確解析:
[1, 2, 3].map(value => ({ number: value, verified: true })) /* <- [ { number: 1, verified: true }, { number: 2, verified: true }, { number: 3, verified: true }] */
該何時(shí)使用箭頭函數(shù)
其實(shí)我們并不應(yīng)該盲目的在一切地方使用ES6,ES6也不是一定比ES5要好,是否使用主要看其能否改善代碼的可讀性和可維護(hù)性。
箭頭函數(shù)也并非適用于所有的情況,比如說(shuō),對(duì)于一個(gè)行數(shù)很多的復(fù)雜函數(shù),使用=>
代替function
關(guān)鍵字帶來(lái)的簡(jiǎn)潔性并不明顯。不過(guò)不得不說(shuō),對(duì)于簡(jiǎn)單函數(shù),箭頭函數(shù)確實(shí)能讓我們的代碼更簡(jiǎn)潔。
給函數(shù)以合理的命名,有助于增強(qiáng)程序的可讀性。箭頭函數(shù)并不能直接命名,但是卻可以通過(guò)賦值給變量的形式實(shí)現(xiàn)間接命名,如下代碼中,我們把箭頭函數(shù)賦值給變量 throwError
,當(dāng)函數(shù)被調(diào)用時(shí),會(huì)拋出錯(cuò)誤,我們可以追溯到是箭頭函數(shù)throwError
報(bào)的錯(cuò)。
var throwError = message => { throw new Error(message) } throwError('this is a warning') <- Uncaught Error: this is a warning at throwError
如果你想完全控制你的函數(shù)中的this
,使用箭頭函數(shù)是簡(jiǎn)潔高效的,采用函數(shù)式編程尤其如此。
[1, 2, 3, 4] .map(value => value * 2) .filter(value => value > 2) .forEach(value => console.log(value))// <- 4// <- 6// <- 8
解構(gòu)賦值
ES6提供的最靈活和富于表現(xiàn)性的新特性莫過(guò)于解構(gòu)了。一旦你熟悉了,它用起來(lái)也很簡(jiǎn)單,某種程度上解構(gòu)可以看做是變量賦值的語(yǔ)法糖,可應(yīng)用于對(duì)象,數(shù)組甚至函數(shù)的參數(shù)。
對(duì)象解構(gòu)
為了更好的描述對(duì)象解構(gòu)如何使用,我們先構(gòu)建下面這樣一個(gè)對(duì)象(漫威迷一定知道這個(gè)對(duì)象描述的是誰(shuí)):
// 描述Bruce Wayne的對(duì)象 var character = { name: 'Bruce', pseudonym: 'Batman', metadata: { age: 34, gender: 'male' }, batarang: ['gas pellet', 'bat-mobile control', 'bat-cuffs'] }
假如現(xiàn)有有一個(gè)名為 pseudonym
的變量,我們想讓其變量值指向character.pseudonym
,使用ES5,你往往會(huì)按下面這樣做:
var pseudonym = character.pseudonym
ES6致力于讓我們的代碼更簡(jiǎn)潔,通過(guò)ES6我們可以用下面的代碼實(shí)現(xiàn)一樣的功能:
var { pseudonym } = character
如同你可以使用var
加逗號(hào)在一行中同時(shí)聲明多個(gè)變量,解構(gòu)的花括號(hào)內(nèi)使用逗號(hào)可以做一樣的事情。
var { pseudonym, name } = character
我們還可以混用解構(gòu)和常規(guī)的自定義變量,這也是解構(gòu)語(yǔ)法靈活性的表現(xiàn)之一。
var { pseudonym } = character, two = 2
解構(gòu)還允許我們使用別名,比如我們想把character.pseudonym
賦值給變量 alias
,可以按下面的語(yǔ)句這樣做,只需要在pseudonym
后面加上:
即可:
var { pseudonym: alias } = character console.log(alias) // <- 'Batman'
解構(gòu)還有另外一個(gè)強(qiáng)大的功能,解構(gòu)值還可以是對(duì)象:
var { metadata: { gender } } = character
當(dāng)然,對(duì)于多層解構(gòu),我們同樣可以賦予別名,這樣我們可以通過(guò)非常簡(jiǎn)潔的方法修改子屬性的名稱(chēng):
var { metadata: { gender: characterGender } } = character
在ES5 中,當(dāng)你調(diào)用一個(gè)未曾聲明的值時(shí),你會(huì)得到undefined
:
console.log(character.boots) // <- undefined console.log(character['boots']) // <- undefined
使用解構(gòu),情況也是類(lèi)似的,如果你在左邊聲明了一個(gè)右邊對(duì)象中不存在的屬性,你也會(huì)得到undefined
.
var { boots } = character console.log(boots) // <- undefined
對(duì)于多層解構(gòu),如下述代碼中,boots
并不存在于character
中,這時(shí)程序會(huì)拋出異常,這就好比你你調(diào)用undefined
或者null
的屬性時(shí)會(huì)出現(xiàn)異常。
var { boots: { size } } = character // <- Exception var { missing } = null // <- Exception
解構(gòu)其實(shí)就是一種語(yǔ)法糖,看以下代碼,你肯定就能很快理解為什么會(huì)拋出異常了。
var nothing = null var missing = nothing.missing // <- Exception
解構(gòu)也可以添加默認(rèn)值,如果右側(cè)不存在對(duì)應(yīng)的值,默認(rèn)值就會(huì)生效,添加的默認(rèn)值可以是數(shù)值,字符串,函數(shù),對(duì)象,也可以是某一個(gè)已經(jīng)存在的變量:
var { boots = { size: 10 } } = character console.log(boots) // <- { size: 10 }
對(duì)于多層的解構(gòu),同樣可以使用默認(rèn)值
var { metadata: { enemy = 'Satan' } } = character console.log(enemy) // <- 'Satan'
默認(rèn)值和別名也可以一起使用,不過(guò)需要注意的是別名要放在前面,默認(rèn)值添加給別名:
var { boots: footwear = { size: 10 } } = character
對(duì)象解構(gòu)同樣支持計(jì)算屬性名,但是這時(shí)候你必須要添加別名,這是因?yàn)橛?jì)算屬性名允許任何類(lèi)似的表達(dá)式,不添加別名,瀏覽器解析時(shí)會(huì)有問(wèn)題,使用如下:
var { ['boo' + 'ts']: characterBoots } = character console.log(characterBoots) // <- true
還是那句話,我們也不是任何情況下都應(yīng)該使用解構(gòu),語(yǔ)句characterBoots = character[type]
看起來(lái)比{ [type]: characterBoots } = character
語(yǔ)義更清晰,但是當(dāng)你需要提取對(duì)象中的子對(duì)象時(shí),解構(gòu)就很簡(jiǎn)潔方便了。
我們?cè)倏纯丛跀?shù)組中該如何使用解構(gòu)。
數(shù)組解構(gòu)
數(shù)組解構(gòu)的語(yǔ)法和對(duì)象解構(gòu)是類(lèi)似的。區(qū)別在于,數(shù)組解構(gòu)我們使用中括號(hào)而非花括號(hào),下面的代碼中,通過(guò)結(jié)構(gòu),我們?cè)跀?shù)組coordinates
中提出了變量 x,y
。 你不需要使用x = coordinates[0]
這樣的語(yǔ)法了,數(shù)組解構(gòu)不使用索引值,但卻讓你的代碼更加清晰。
var coordinates = [12, -7] var [x, y] = coordinates console.log(x) // <- 12
數(shù)組解構(gòu)也允許你跳過(guò)你不想用到的值,在對(duì)應(yīng)地方留白即可:
var names = ['James', 'L.', 'Howlett'] var [ firstName, , lastName ] = names console.log(lastName) // <- 'Howlett'
和對(duì)象解構(gòu)一樣,數(shù)組解構(gòu)也允許你添加默認(rèn)值:
var names = ['James', 'L.'] var [ firstName = 'John', , lastName = 'Doe' ] = names console.log(lastName) // <- 'Doe'
在ES5中,你需要借助第三個(gè)變量,才能完成兩個(gè)變量值的交換,如下:
var left = 5, right = 7; var aux = left left = right right = aux
使用解構(gòu),一切就簡(jiǎn)單多了:
var left = 5, right = 7; [left, right] = [right, left]
我們?cè)倏纯春瘮?shù)解構(gòu)。
函數(shù)默認(rèn)參數(shù)
在ES6中,我們可以給函數(shù)的參數(shù)添加默認(rèn)值了,下例中我們就給參數(shù) exponent
分配了一個(gè)默認(rèn)值:
function powerOf(base, exponent = 2) { return Math.pow(base, exponent) }
箭頭函數(shù)同樣支持使用默認(rèn)值,需要注意的是,就算只有一個(gè)參數(shù),如果要給參數(shù)添加默認(rèn)值,參數(shù)部分一定要用小括號(hào)括起來(lái)。
var double = (input = 0) => input * 2
我們可以給任何位置的任何參數(shù)添加默認(rèn)值。
function sumOf(a = 1, b = 2, c = 3) { return a + b + c } console.log(sumOf(undefined, undefined, 4)) // <- 1 + 2 + 4 = 7
在JS中,給一個(gè)函數(shù)提供一個(gè)包含若干屬性的對(duì)象字面量做為參數(shù)的情況并不常見(jiàn),不過(guò)你依舊可以按下面方法這樣做:
var defaultOptions = { brand: 'Volkswagen', make: 1999 } function carFactory(options = defaultOptions) { console.log(options.brand) console.log(options.make) } carFactory() // <- 'Volkswagen' // <- 1999
不過(guò)這樣做存在一定的問(wèn)題,當(dāng)你調(diào)用該函數(shù)時(shí),如果傳入的參數(shù)對(duì)象只包含一個(gè)屬性,另一個(gè)屬性的默認(rèn)值會(huì)自動(dòng)失效:
carFactory({ make: 2000 }) // <- undefined // <- 2000
函數(shù)參數(shù)解構(gòu)就可以解決這個(gè)問(wèn)題。
函數(shù)參數(shù)解構(gòu)
通過(guò)函數(shù)參數(shù)解構(gòu),可以解決上面的問(wèn)題,這里我們?yōu)槊恳粋€(gè)屬性都提供了默認(rèn)值,單獨(dú)改變其中一個(gè)并不會(huì)影響其它的值:
function carFactory({ brand = 'Volkswagen', make = 1999 }) { console.log(brand) console.log(make) } carFactory({ make: 2000 }) // <- 'Volkswagen' // <- 2000
不過(guò)這種情況下,函數(shù)調(diào)用時(shí),如果參數(shù)為空即carFactory()
函數(shù)將拋出異常。這種問(wèn)題可以通過(guò)下面的方法來(lái)修復(fù),下述代碼中我們添加了一個(gè)空對(duì)象作為options
的默認(rèn)值,這樣當(dāng)函數(shù)被調(diào)用時(shí),如果參數(shù)為空,會(huì)自動(dòng)以{}
作為參數(shù)。
function carFactory({ brand = 'Volkswagen', make = 1999 } = {}) { console.log(brand) console.log(make) } carFactory() // <- 'Volkswagen' // <- 1999
除此之外,使用函數(shù)參數(shù)解構(gòu),還可以讓你的函數(shù)自行匹配對(duì)應(yīng)的參數(shù),看接下來(lái)的例子,你就能明白這一點(diǎn)了,我們定義一個(gè)名為car
的對(duì)象,這個(gè)對(duì)象擁有很多屬性:owner,brand,make,model,preferences等等。
var car = { owner: { id: 'e2c3503a4181968c', name: 'Donald Draper' }, brand: 'Peugeot', make: 2015, model: '208', preferences: { airbags: true, airconditioning: false, color: 'red' } }
解構(gòu)能讓我們的函數(shù)方便的只使用里面的部分?jǐn)?shù)據(jù),下面代碼中的函數(shù)getCarProductModel
說(shuō)明了具體該如何使用:
var getCarProductModel = ({ brand, make, model }) => ({ sku: brand + ':' + make + ':' + model, brand, make, model }) getCarProductModel(car)
解構(gòu)使用示例
當(dāng)一個(gè)函數(shù)的返回值為對(duì)象或者數(shù)組時(shí),使用解構(gòu),我們可以非常簡(jiǎn)潔的獲取返回對(duì)象中某個(gè)屬性的值(返回?cái)?shù)組中某一項(xiàng)的值)。比如說(shuō),函數(shù)getCoordinates()
返回了一系列的值,但是我們只想用其中的x,y
,我們可以這樣寫(xiě),解構(gòu)幫助我們避免了很多中間變量的使用,也使得我們代碼的可讀性更高。
function getCoordinates() { return { x: 10, y: 22, z: -1, type: '3d' } } var { x, y } = getCoordinates()
通過(guò)使用默認(rèn)值,可以減少重復(fù),比如你想寫(xiě)一個(gè)random
函數(shù),這個(gè)函數(shù)將返回一個(gè)位于min
和max
之間的值。我們可以分辨設(shè)置min
默認(rèn)值為1,max
默認(rèn)值為10,在需要的時(shí)候還可以單獨(dú)改變其中的某一個(gè)值:
function random({ min = 1, max = 10 } = {}) { return Math.floor(Math.random() * (max - min)) + min } console.log(random())// <- 7 console.log(random({ max: 24 }))// <- 18
解構(gòu)還可以配合正則表達(dá)式使用??聪旅孢@個(gè)例子:
function splitDate(date) { var rdate = /(\d+).(\d+).(\d+)/ return rdate.exec(date) } var [ , year, month, day] = splitDate('2015-11-06')
不過(guò)當(dāng).exec
不比配時(shí)會(huì)返回null
,因此我們需要修改上述代碼如下:
var matches = splitDate('2015-11-06')if (matches === null) { return } var [, year, month, day] = matches
下面我們繼續(xù)來(lái)講講spread
和rest
操作符。
剩余參數(shù)和拓展符
ES6之前,對(duì)于不確定數(shù)量參數(shù)的函數(shù)。你需要使用偽數(shù)組arguments
,它擁有length
屬性,卻又不具備很多一般數(shù)組有的特性。需要通過(guò)Array#slice.call
轉(zhuǎn)換arguments
對(duì)象真數(shù)組后才能進(jìn)行下一步的操作:
function join() { var list = Array.prototype.slice.call(arguments) return list.join(', ') } join('first', 'second', 'third')// <- 'first, second, third'
對(duì)于這種情況,ES6提供了一種更好的解決方案:rest
。
剩余參數(shù)rest
使用rest
, 你只需要在任意JavaScript函數(shù)的最后一個(gè)參數(shù)前添加三個(gè)點(diǎn)...
即可。當(dāng)rest
參數(shù)是函數(shù)的唯一參數(shù)時(shí),它就代表了傳遞給這個(gè)函數(shù)的所有參數(shù)。它起到和前面說(shuō)的.slice
一樣的作用,把參數(shù)轉(zhuǎn)換為了數(shù)組,不需要你再對(duì)arguments
進(jìn)行額外的轉(zhuǎn)換了。
function join(...list) { return list.join(', ') } join('first', 'second', 'third')// <- 'first, second, third'
rest
參數(shù)之前的命名參數(shù)不會(huì)被包含在rest
中,
function join(separator, ...list) { return list.join(separator) } join('; ', 'first', 'second', 'third')// <- 'first; second; third'
在箭頭函數(shù)中使用rest
參數(shù)時(shí),即使只有這一個(gè)參數(shù),也需要使用圓括號(hào)把它圍起來(lái),不然就會(huì)報(bào)錯(cuò)SyntaxError
,使用示例如下:
var sumAll = (...numbers) => numbers.reduce( (total, next) => total + next ) console.log(sumAll(1, 2, 5))// <- 8
上述代碼的ES5實(shí)現(xiàn)如下:
// ES5的寫(xiě)法 function sumAll() { var numbers = Array.prototype.slice.call(arguments) return numbers.reduce(function (total, next) { return total + next }) } console.log(sumAll(1, 2, 5))// <- 8
拓展運(yùn)算符
拓展運(yùn)算符可以把任意可枚舉對(duì)象轉(zhuǎn)換為數(shù)組,使用拓展運(yùn)算符可以高效處理目標(biāo)對(duì)象,在拓展目前前添加...
就可以使用拓展運(yùn)算符了。下例中...arguments
就把函數(shù)的參數(shù)轉(zhuǎn)換為了數(shù)組字面量。
function cast() { return [...arguments] } cast('a', 'b', 'c')// <- ['a', 'b', 'c']
使用拓展運(yùn)算符,我們也可以把字符串轉(zhuǎn)換為由每一個(gè)字母組成的數(shù)組:
[...'show me']// <- ['s', 'h', 'o', 'w', ' ', 'm', 'e']
使用拓展運(yùn)算符,還可以拼合數(shù)組:
function cast() { return ['left', ...arguments, 'right'] } cast('a', 'b', 'c')// <- ['left', 'a', 'b', 'c', 'right']
var all = [1, ...[2, 3], 4, ...[5], 6, 7] console.log(all)// <- [1, 2, 3, 4, 5, 6, 7]
這里我還想再?gòu)?qiáng)調(diào)一下,拓展運(yùn)算符不僅僅適用于數(shù)組和arguments
對(duì)象,對(duì)任意可迭代的對(duì)象都可以使用。迭代也是ES6新提出的一個(gè)概念,在[ Iteration and Flow Control]()這一章,我們將詳細(xì)敘述迭代。
Shifting和Spreading
當(dāng)你想要抽出一個(gè)數(shù)組的前一個(gè)或者兩個(gè)元素時(shí),常用的解決方案是使用.shift
.盡管是函數(shù)式的,下述代碼在第一次看到的時(shí)候卻不好理解,我們使用了兩次.slice
從list
中抽離出兩個(gè)不同的元素。
var list = ['a', 'b', 'c', 'd', 'e'] var first = list.shift() var second = list.shift() console.log(first)// <- 'a'
在ES6中,結(jié)合使用拓展和解構(gòu),可以讓代碼的可讀性更好:
var [first, second, ...other] = ['a', 'b', 'c', 'd', 'e'] console.log(other)// <- ['c', 'd', 'e']
除了對(duì)數(shù)組進(jìn)行拓展,你同樣可以對(duì)函數(shù)參數(shù)使用拓展,下例展示了如何添加任意數(shù)量的參數(shù)到multiply
函數(shù)中。
function multiply(left, right) { return left * right } var result = multiply(...[2, 3]) console.log(result)// <- 6
向在數(shù)組中一樣,函數(shù)參數(shù)中的拓展運(yùn)算符同樣可以結(jié)合常規(guī)參數(shù)一起使用。下例中,print
函數(shù)結(jié)合使用了rest
,普通參數(shù),和拓展運(yùn)算符:
function print(...list) { console.log(list) } print(1, ...[2, 3], 4, ...[5])// <- [1, 2, 3, 4, 5]
下表總結(jié)了,拓展運(yùn)算符的常見(jiàn)使用方法:
使用示例 | ES5 | ES6 |
---|---|---|
Concatenation | [1, 2].concat(more) | [1, 2, ...more] |
Push an array onto list | list.push.apply(list, items) | list.push(...items) |
Destructuring | a = list[0], other = list.slice(1) | <span class="Apple-tab-span" style="white-space: pre;"> </span>[a, ...other] = list |
new and apply | new (Date.bind.apply(Date, [null,2015,31,8])) | new Date(...[2015,31,8]) |
模板字符串
模板字符串是對(duì)常規(guī)JavaScript
字符串的重大改進(jìn),不同于在普通字符串中使用單引號(hào)或者雙引號(hào),模板字符串的聲明需要使用反撇號(hào),如下所示:
var text = `This is my first template literal`
因?yàn)槭褂玫氖欠雌蔡?hào),你可以在模板字符串中隨意使用單雙引號(hào)了,使用時(shí)不再需要考慮轉(zhuǎn)義,如下:
var text = `I'm "amazed" at these opportunities!`
模板字符串具有很多強(qiáng)大的功能,可在其中插入JavaScript表達(dá)式就是其一。
在字符串中插值
通過(guò)模板字符串,你可以在模板中插入任何JavaScript表達(dá)式了。當(dāng)解析到表達(dá)式時(shí),表達(dá)式會(huì)被執(zhí)行,該處將渲染表達(dá)式的值,下例中,我們?cè)谧址胁迦肓俗兞?code>name:
var name = 'Shannon' var text = `Hello, ${ name }!` console.log(text)// <- 'Hello, Shannon!'
模板字符串是支持任何表達(dá)式的。使用模板字符串,代碼將更容易維護(hù),你無(wú)須再手動(dòng)連接字符串和JavaScript表達(dá)式了。
看下面插入日期的例子,是不是又直觀又方便:
`The time and date is ${ new Date().toLocaleString() }.`// <- 'the time and date is 8/26/2015, 3:15:20 PM'
表達(dá)式中還可以包含數(shù)學(xué)運(yùn)算符:
`The result of 2+3 equals ${ 2 + 3 }`// <- 'The result of 2+3 equals 5'
鑒于模板字符串本身也是JavaScript表達(dá)式,我們?cè)谀0遄址羞€可以嵌套模板字符串;
`This template literal ${ `is ${ 'nested' }` }!`// <- 'This template literal is nested!'
模板字符串的另外一個(gè)優(yōu)點(diǎn)是支持多行字符串;
多行文本模板
在ES6之前,如果你想表現(xiàn)多行字符串,你需要使用轉(zhuǎn)義,數(shù)組拼合,甚至使用使用注釋符做復(fù)雜的hacks.如下所示:
var escaped ='The first line\n\ A second line\n\ Then a third line' var concatenated ='The first line\n' ` 'A second line\n' `'Then a third line' var joined = ['The first line','A second line','Then a third line'].join('\n')
應(yīng)用ES6,這種處理就簡(jiǎn)單多了,模板字符串默認(rèn)支持多行:
var multiline =`The first line A second line Then a third line`
當(dāng)你需要返回的字符串基于html
和數(shù)據(jù)生成,使用模板字符串是很簡(jiǎn)潔高效的,如下所示:
var book = { title: 'Modular ES6', excerpt: 'Here goes some properly sanitized HTML', tags: ['es6', 'template-literals', 'es6-in-depth'] } var html = `<article> <header> <h1>${ book.title }</h1> </header> <section>${ book.excerpt }</section> <footer> <ul> ${ book.tags .map(tag => `<li>${ tag }</li>`) .join('\n ') } </ul> </footer> </article>`
上述代碼將得到下面這樣的結(jié)果??崭竦靡员A?,多個(gè)li
也按我們的預(yù)期被合適的渲染:
<article> <header> <h1>Modular ES6</h1> </header> <section>Here goes some properly sanitized HTML</section> <footer> <ul> <li>es6</li> <li>template-literals</li> <li>es6-in-depth</li> </ul> </footer> </article>
不過(guò)有時(shí)候我們并不希望空格被保留,下例中我們?cè)诤瘮?shù)中使用包含縮進(jìn)的模板字符串,我們希望結(jié)果沒(méi)有縮進(jìn),但是實(shí)際的結(jié)果卻有四格的縮進(jìn)。
function getParagraph() { return ` Dear Rod, This is a template literal string that's indented four spaces. However, you may have expected for it to be not indented at all. Nico `}
我們可以用下面這個(gè)功能函數(shù)對(duì)生成的字符串進(jìn)行處理已得到我們想要的結(jié)果:
function unindent(text) { return text .split('\n') .map(line => line.slice(4)) .join('\n') .trim() }
不過(guò),使用被稱(chēng)為標(biāo)記模板的模板字符串新特性處理這種情況可能會(huì)更好。
標(biāo)記模板
默認(rèn)情況下,JavaScript會(huì)把\
解析為轉(zhuǎn)義符號(hào),對(duì)瀏覽器來(lái)說(shuō),以\
開(kāi)頭的字符一般具有特殊的含義。比如說(shuō)\n
意味著新行,\u00f1
表示?
等等。如果你不想瀏覽器執(zhí)行這種特殊解析,你也可以使用String.raw
來(lái)標(biāo)記模板。下面的代碼就是這樣做的,這里我們使用了String.row
來(lái)處理模板字符串,相應(yīng)的這里面的\n
沒(méi)有被解析為新行。
var text = String.raw`"\n" is taken literally. It'll be escaped instead of interpreted.` console.log(text) // "\n" is taken literally. // It'll be escaped instead of interpreted.
我們添加在模板字符串之前的String.raw
前綴,這就是標(biāo)記模板,這樣的模板字符串在被渲染前被該標(biāo)記代表的函數(shù)預(yù)處理。
一個(gè)典型的標(biāo)記模板字符串如下:
tag`Hello, ${ name }. I am ${ emotion } to meet you!`
實(shí)際上,上面標(biāo)記模板可以用以下函數(shù)形式表示:
tag( ['Hello, ', '. I am ', ' to meet you!'], 'Maurice', 'thrilled' )
我們還是用代碼來(lái)說(shuō)明這個(gè)概念,下述代碼中,我們先定義一個(gè)名為tag
函數(shù):
function tag(parts, ...values) { return parts.reduce( (all, part, index) => all + values[index - 1] + part ) }
然后我們調(diào)用使用使用標(biāo)記模板,不過(guò)此時(shí)的結(jié)果和不使用標(biāo)記模板是一樣的,這是因?yàn)槲覀兌x的tag
函數(shù)實(shí)際上并未對(duì)字符串進(jìn)行額外的處理。
var name = 'Maurice' var emotion = 'thrilled' var text = tag`Hello, ${ name }. I am ${ emotion } to meet you!` console.log(text)// <- 'Hello Maurice, I am thrilled to meet you!'
我們看一個(gè)進(jìn)行額外處理的例子,比如轉(zhuǎn)換所有用戶(hù)輸入的值為大寫(xiě)(假設(shè)用戶(hù)只會(huì)輸入英語(yǔ)),這里我們定義標(biāo)記函數(shù)upper
來(lái)做這件事:
function upper(parts, ...values) { return parts.reduce((all, part, index) => all + values[index - 1].toUpperCase() + part ) } var name = 'Maurice' var emotion = 'thrilled' upper`Hello, ${ name }. I am ${ emotion } to meet you!` // <- 'Hello MAURICE, I am THRILLED to meet you!'
既然可以轉(zhuǎn)換輸入為大寫(xiě),那我們?cè)龠M(jìn)一步想想,如果提供合適的標(biāo)記模板函數(shù),使用標(biāo)記模板,我們還可以對(duì)模板中的表達(dá)式進(jìn)行各種過(guò)濾處理,比如有這么一個(gè)場(chǎng)景,假設(shè)表達(dá)式的值都來(lái)自用戶(hù)輸入,假設(shè)有一個(gè)名為sanitize
的庫(kù)可用于去除用戶(hù)輸入中的html標(biāo)簽,那通過(guò)使用標(biāo)記模板,就可以有效的防止XSS攻擊了,使用方法如下。
function sanitized(parts, ...values) { return parts.reduce((all, part, index) => all + sanitize(values[index - 1]) + part ) } var comment = 'Evil comment<iframe src="http://evil.corp"> </iframe>' var html = sanitized`<div>${ comment }</div>`console.log(html) // <- '<div>Evil comment</div>'
ES6中的另外一個(gè)大的改變是提供了新的變量聲明方式:let
和const
聲明,下面我們一起來(lái)學(xué)習(xí)。
let
& const
聲明
可能很早之前你就聽(tīng)說(shuō)過(guò) let
了,它用起來(lái)像 var
但是,卻有不同的作用域規(guī)則。
JavaScript的作用域有一套復(fù)雜的規(guī)則,變量提升的存在常常讓新手忐忑不安。變量提升,意味著無(wú)論你在那里聲明的變量,在瀏覽器解析時(shí),實(shí)際上都被提升到了當(dāng)前作用域的頂部被聲明??聪旅娴倪@個(gè)例子:
function isItTwo(value) { if (value === 2) { var two = true } return two } isItTwo(2)// <- true isItTwo('two')// <- undefined
盡管two
是在代碼分支中被聲明,之后被外部分支引用,上述的JS代碼還是可以工作的。var
聲明的變量two
實(shí)際是在isItTwo
頂部被聲明的。由于聲明提升的存在,上述代碼其實(shí)和下面代碼的效果是一樣的
function isItTwo(value) { var two if (value === 2) { two = true } return two }
帶來(lái)了靈活性的同事,變量提升也帶來(lái)了更大的迷惑性,還好ES6 為我們提供了塊作用域。
塊作用域和let
聲明
相比函數(shù)作用域,塊作用域允許我們通過(guò)if
,for
,while
聲明創(chuàng)建新作用域,甚至任意創(chuàng)建{}
塊也能創(chuàng)建新的作用域:
{{{{{ var deep = 'This is available from outer scope.'; }}}}} console.log(deep) // <- 'This is available from outer scope.'
由于這里使用的是var
,考慮到變量提升的存在,我們?cè)谕獠恳琅f可以讀取到深層中的deep
變量,這里并不會(huì)報(bào)錯(cuò)。不過(guò)在以下情況下,我們可能希望這里會(huì)報(bào)錯(cuò):
訪問(wèn)內(nèi)部變量會(huì)打破我們代碼中的某種封裝原則;
父塊中已有有一個(gè)一個(gè)同名變量,但是內(nèi)部也需要用同名變量;
使用let
就可以解決這個(gè)問(wèn)題,let
創(chuàng)建的變量在塊作用域內(nèi)有效,在ES6提出let
以前,想要?jiǎng)?chuàng)建深層作用域的唯一辦法就是再新建一個(gè)函數(shù)。使用let
,你只需添加另外一對(duì){}
:
let topmost = {} { let inner = {} { let innermost = {} } // attempts to access innermost here would throw } // attempts to access inner here would throw // attempts to access innermost here would throw
在for
循環(huán)中使用let
是一個(gè)很好的實(shí)踐,這樣定義的變量只會(huì)在當(dāng)前塊作用域內(nèi)生效。
for (let i = 0; i < 2; i++) { console.log(i) // <- 0 // <- 1 } console.log(i)// <- i is not defined
考慮到let
聲明的變量在每一次循環(huán)的過(guò)程中都重復(fù)聲明,這在處理異步函數(shù)時(shí)就很有效,不會(huì)發(fā)生使用var
時(shí)產(chǎn)生的詭異的結(jié)果,我們看一個(gè)具體的例子。
我們先看看 var
聲明的變量是怎么工作的,下述代碼中 i
變量 被綁定在 printNumber
函數(shù)作用域中,當(dāng)每個(gè)回調(diào)函數(shù)被調(diào)用時(shí),它的值會(huì)逐步升到10,但是當(dāng)每個(gè)回調(diào)函數(shù)運(yùn)行時(shí)(每100us),此時(shí)的i
的值已經(jīng)是10了,因此每次打印的結(jié)果都是10.
function printNumbers() { for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(i) }, i * 100) } } printNumbers()
使用let
,則會(huì)把i
綁定到每一個(gè)塊作用域中。每一次循環(huán) i
的值還是在增加,但是每次其實(shí)都是創(chuàng)建了一個(gè)新的 i
,不同的 i
之間不會(huì)相互影響 ,因此打印出的就是預(yù)想的0到9了。
function printNumbers() { for (let i = 0; i < 10; i++) { setTimeout(function () { console.log(i) }, i * 100) } } printNumbers()
為了細(xì)致的講述let
的工作原理, 我們還需要弄懂一個(gè)名為 Temporal Dead Zone
的概念。
Temporal Dead Zone
簡(jiǎn)言之,如果你的代碼類(lèi)似下面這樣,就會(huì)報(bào)錯(cuò)。即在某個(gè)作用域中,在let
聲明之前調(diào)用了let
聲明的變量,導(dǎo)致的問(wèn)題就是由于,Temporal Dead Zone(TDZ)的存在。
{ console.log(name) // <- ReferenceError: name is not defined let name = 'Stephen Hawking' }
如果定義的是一個(gè)函數(shù),函數(shù)中引用了name
變量則是可以的,但是這個(gè)函數(shù)并未在聲明前執(zhí)行則不會(huì)報(bào)錯(cuò)。如果let
聲明之前就調(diào)用了該函數(shù),同樣會(huì)導(dǎo)致TDZ。
// 不會(huì)報(bào)錯(cuò) function readName() { return name } let name = 'Stephen Hawking' console.log(readName()) // <- 'Stephen Hawking'
// 會(huì)報(bào)錯(cuò) function readName() { return name } console.log(readName()) // ReferenceError: name is not defined let name = 'Stephen Hawking'
即使像下面這樣let
定義的變量沒(méi)有被賦值,下面的代碼也會(huì)報(bào)錯(cuò),原因依舊是它試圖在聲明前訪問(wèn)一個(gè)被let
定義的變量
function readName() { return name } console.log(readName())// ReferenceError: name is not definedlet name
下面的代碼則是可行的:
function readName() { return name } let nameconsole.log(readName())// <- undefined
TDZ的存在使得程序更容易報(bào)錯(cuò),由于聲明提升和不好的編碼習(xí)慣常常會(huì)存在這樣的問(wèn)題。在ES6中則可以比較好的避免了這種問(wèn)題了,需要注意的是let
聲明的變量同樣存在聲明提升。這意味著,變量會(huì)在我們進(jìn)入塊作用域時(shí)就會(huì)創(chuàng)建,TDZ也是在這時(shí)候創(chuàng)建的,它保證該變量不許被訪問(wèn),只有在代碼運(yùn)行到let
聲明所在位置時(shí),這時(shí)候TDZ才會(huì)消失,訪問(wèn)限制才會(huì)取消,變量才可以被訪問(wèn)。
Const 聲明
const
聲明也具有類(lèi)似let
的塊作用域,它同樣具有TDZ
機(jī)制。實(shí)際上,TDZ機(jī)制是因?yàn)?code>const才被創(chuàng)建,隨后才被應(yīng)用到let
聲明中。const
需要TDZ的原因是為了防止由于變量提升,在程序解析到const
語(yǔ)句之前,對(duì)const
聲明的變量進(jìn)行了賦值操作,這樣是有問(wèn)題的。
下面的代碼表明,const
具有和let
一致的塊作用域:
const pi = 3.1415{ const pi = 6 console.log(pi) // <- 6 } console.log(pi)// <- 3.1415
下面我們說(shuō)說(shuō)const
和let
的主要區(qū)別,首先const
聲明的變量在聲明時(shí)必須賦值,否則會(huì)報(bào)錯(cuò):
const pi = 3.1415 const e // SyntaxError, missing initializer
除了必須初始化,被const
聲明的變量不能再被賦予別的值。在嚴(yán)格模式下,試圖改變const
聲明的變量會(huì)直接報(bào)錯(cuò),在非嚴(yán)格模式下,改變被靜默被忽略。
const people = ['Tesla', 'Musk'] people = [] console.log(people)// <- ['Tesla', 'Musk']
請(qǐng)注意,const
聲明的變量并非意味著,其對(duì)應(yīng)的值是不可變的。真正不能變的是對(duì)該值的引用,下面我們具體說(shuō)明這一點(diǎn)。
通過(guò)const聲明的變量值并非不可改變
使用const
只是意味著,變量將始終指向相同的對(duì)象或初始的值。這種引用是不可變的。但是值并非不可變。
下面的例子說(shuō)明,雖然people
的指向不可變,但是數(shù)組本身是可以被修改的。
const people = ['Tesla', 'Musk'] people.push('Berners-Lee') console.log(people)// <- ['Tesla', 'Musk', 'Berners-Lee']
const
只是阻止變量引用另外一個(gè)值,下例中,盡管我們使用const
聲明了people
,然后把它賦值給了humans
,我們還是可以改變humans
的指向,因?yàn)?code>humans不是由const
聲明的,其引用可隨意改變。people
是由 const
聲明的,則不可改變。
const people = ['Tesla', 'Musk'] var humans = people humans = 'evil' console.log(humans)// <- 'evil'
如果我們的目的是讓值不可修改,我們需要借助函數(shù)的幫助,比如使用Object.freeze
:
const frozen = Object.freeze( ['Ice', 'Icicle', 'Ice cube'] ) frozen.push('Water') // Uncaught TypeError: Can't add property 3 // object is not extensible
下面我們?cè)敿?xì)討論一下const
和let
的優(yōu)點(diǎn)
const
和let
的優(yōu)點(diǎn)
新功能并不應(yīng)該因?yàn)槭切鹿δ芏皇褂?,ES6語(yǔ)法被使用的前提是它可以顯著的提升我們代碼的可讀寫(xiě)和可維護(hù)性。let
聲明在大多數(shù)情況下,可以替換var
以避免預(yù)期之外的問(wèn)題。使用let
你可以把聲明在塊的頂部進(jìn)行而非函數(shù)的頂部進(jìn)行。
有時(shí),我們希望有些變量的引用不可變,這時(shí)候使用const
就能防止很多問(wèn)題的發(fā)生。下述代碼中 在checklist
函數(shù)外給items
變量傳遞引用時(shí)就非常容易出錯(cuò),它返回的todo
API和items
有了交互。當(dāng)items
變量被改為指向另外一個(gè)列表時(shí),我們的代碼就出問(wèn)題了。todo
API 用的還是items
之前的值,items
本身的指代則已經(jīng)改變。
var items = ['a', 'b', 'c'] var todo = checklist(items) todo.check() console.log(items) // <- ['b', 'c'] items = ['d', 'e'] todo.check() console.log(items) // <- ['d', 'e'], would be ['c'] if items had been constant function checklist(items) { return { check: () => items.shift() } }
這類(lèi)問(wèn)題很難debug,找到問(wèn)題原因就會(huì)花費(fèi)你很長(zhǎng)一段時(shí)間。使用const
運(yùn)行時(shí)就會(huì)報(bào)錯(cuò),可以幫助你可以避免這種問(wèn)題。
如果我們默認(rèn)只使用cosnt
和let
聲明變量,所有的變量都會(huì)有一樣的作用域規(guī)則,這讓代碼更易理解,由于const
造成的影響最小,它還曾被提議作為默認(rèn)的變量聲明。
總的來(lái)說(shuō),const
不允許重新指定值,使用的是塊作用域,存在TDZ。let
則允許重新指定值,其它方面和const
類(lèi)似,而var
聲明使用函數(shù)作用域,可以重新指定值,可以在未聲明前調(diào)用,考慮到這些,推薦盡量不要使用var
聲明了。
【相關(guān)推薦:javascript視頻教程、編程視頻】
Atas ialah kandungan terperinci Apakah gula sintaksis yang ada pada es6?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undress AI Tool
Gambar buka pakaian secara percuma

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Dalam ES6, anda boleh menggunakan kaedah reverse() objek tatasusunan untuk mencapai pembalikan tatasusunan Kaedah ini digunakan untuk membalikkan susunan elemen dalam tatasusunan, meletakkan elemen terakhir dahulu dan elemen pertama terakhir .reverse()". Kaedah reverse() akan mengubah suai tatasusunan asal Jika anda tidak mahu mengubah suainya, anda perlu menggunakannya dengan operator pengembangan "..." dan sintaksnya ialah "[...array].reverse() ".

async ialah es7. async dan await ialah penambahan baharu kepada ES7 dan merupakan penyelesaian untuk operasi asynchronous/wait boleh dikatakan sebagai gula sintaktik untuk modul bersama dan fungsi penjana, menyelesaikan kod tak segerak dengan semantik yang lebih jelas. Seperti namanya, async bermaksud "tak segerak".

Untuk keserasian pelayar. Sebagai spesifikasi baharu untuk JS, ES6 menambah banyak sintaks dan API baharu Walau bagaimanapun, penyemak imbas moden tidak mempunyai sokongan tinggi untuk ciri baharu ES6, jadi kod ES6 perlu ditukar kepada kod ES5. Dalam alat pembangun web WeChat, babel digunakan secara lalai untuk menukar kod sintaks ES6 pembangun kepada kod ES5 yang disokong dengan baik oleh ketiga-tiga terminal, membantu pembangun menyelesaikan masalah pembangunan yang disebabkan oleh persekitaran yang berbeza hanya dalam projek Hanya konfigurasi dan semak Pilihan "ES6 hingga ES5".

Langkah-langkah: 1. Tukar dua tatasusunan untuk menetapkan jenis masing-masing, dengan sintaks "newA=new Set(a);newB=new Set(b);" 2. Gunakan has() dan filter() untuk mencari set perbezaan , dengan sintaks " new Set([...newA].filter(x =>!newB.has(x)))", elemen set perbezaan akan dimasukkan dalam koleksi set dan dikembalikan 3. Gunakan Array. daripada untuk menukar set menjadi Jenis tatasusunan, sintaks "Array.from(collection)".

Dalam es5, anda boleh menggunakan fungsi for dan indexOf() untuk mencapai deduplikasi tatasusunan Sintaks "for(i=0;i<array length;i++){a=newArr.indexOf(arr[i]);if(. a== -1){...}}". Dalam es6, anda boleh menggunakan operator spread, Array.from() dan Set untuk mengalih keluar penduaan anda perlu terlebih dahulu menukar tatasusunan menjadi objek Set untuk mengalih keluar pendua, dan kemudian menggunakan fungsi spread atau Array.from() untuk tukar objek Set kembali kepada kumpulan Just.

Dalam es6, zon mati sementara ialah ralat sintaks, yang merujuk kepada arahan let dan const yang menjadikan blok membentuk skop tertutup. Dalam blok kod, sebelum pembolehubah diisytiharkan menggunakan perintah let/const, pembolehubah tidak tersedia dan tergolong dalam "zon mati" pembolehubah sebelum pembolehubah diisytiharkan ini secara sintaksis dipanggil "zon mati sementara". ES6 menetapkan bahawa promosi pembolehubah tidak berlaku dalam zon mati sementara dan pernyataan let dan const, terutamanya untuk mengurangkan ralat masa jalan dan menghalang pembolehubah daripada digunakan sebelum ia diisytiharkan, yang mungkin membawa kepada tingkah laku yang tidak dijangka.

Tidak, memerlukan sintaks modular bagi spesifikasi CommonJS dan sintaks modular bagi spesifikasi es6 ialah import. memerlukan dimuatkan pada masa jalan, dan import dimuatkan pada masa penyusunan; memerlukan boleh ditulis di mana-mana dalam kod, import hanya boleh ditulis di bahagian atas fail dan tidak boleh digunakan dalam penyataan bersyarat atau skop fungsi diperkenalkan sahaja apabila memerlukan dijalankan Oleh itu, prestasi adalah agak rendah Sifat modul yang diperkenalkan semasa penyusunan import mempunyai prestasi yang lebih tinggi.

Dalam es6, anda boleh menggunakan atribut panjang objek tatasusunan untuk menentukan bilangan item yang terdapat dalam tatasusunan, iaitu, untuk mendapatkan bilangan elemen dalam tatasusunan ini boleh mengembalikan bilangan elemen dalam tatasusunan, hanya gunakan pernyataan "array.length" Mengembalikan nilai yang mewakili bilangan elemen objek tatasusunan, iaitu nilai panjang.
