es6 syntax sugar includes: 1. Object literal, which refers to an object directly expressed in the form of "{}"; 2. Arrow function, a new method of writing anonymous functions; 3. Destructuring assignment, which allows According to a certain pattern, extract values ??from arrays or objects and assign values ??to variables; 4. Remaining parameters and expanders; 5. Template strings; 6. Let and const declaration statements.
The operating environment of this tutorial: Windows 7 system, ECMAScript version 6, Dell G3 computer.
Syntactic sugar: refers to the syntax in a programming language that can more easily express an operation. It can make it easier for programmers to use the language, and the operation can become clearer, more convenient, or more consistent with the program. Programming habits of programmers.
ES6 provides non-destructive updates for some existing functions. Most of these updates can be understood as syntactic sugar, which is called syntactic sugar. It means that this new syntax can do Things can actually be done with ES5, but it will be a little more complicated. In this chapter, we will focus on these syntactic sugars. After reading this, you may have a different understanding of some of the new ES6 syntax that you are familiar with.
Object literal
Object literal refers to an object directly expressed in the form of {}
, such as the following :
var book = { title: 'Modular ES6', author: 'Nicolas', publisher: 'O′Reilly' }
ES6 brings some improvements to the syntax of object literals: including concise representation of properties/methods, computable property names, etc. Let’s look at them one by one:
Concise representation of attributes
Have you ever encountered this scenario? An object we declare contains several attributes, whose attribute values ??are represented by variables, and the variable names and attribute names the same. For example, as shown below, we want to assign an array named listeners
to the listeners
property in the events
object. Using ES5 we will do this:
var listeners = [] function listen() {} var events = { listeners: listeners, listen: listen }
ES6 allows us to abbreviate it in the following form: How about
var listeners = [] function listen() {} var events = { listeners, listen }
, does it feel much simpler? The concise writing method of using object literals allows us to reduce the number of words without affecting the semantics. Repeat the code.
This is one of the benefits of ES6. It provides many simpler and clearer semantic syntaxes, which greatly improves the readability and maintainability of our code.
Computable property names
Another important update to object literals is that they allow you to use computed property names. In ES5 we can also add The attribute name is a variable. Generally speaking, we have to do this in the following way. First declare a variable named expertise
, and then use the form person[expertise]
to Variables are added as attributes of the object person
:
var expertise = 'journalism' var person = { name: 'Sharon', age: 27 } person[expertise] = { years: 5, interests: ['international', 'politics', 'internet'] }
In ES6, object literals can use calculated property names. Put any expression in square brackets, and the result of the expression will be will be the corresponding attribute name. The above code can be written like this in ES6:
var expertise = 'journalism' var person = { name: 'Sharon', age: 27, [expertise]: { years: 5, interests: ['international', 'politics', 'internet'] } }
However, it should be noted that abbreviated attributes and calculated attribute names cannot be used at the same time . This is because the abbreviated property is a syntactic sugar that takes effect at the compile stage, while the calculated property name takes effect at runtime. If you mix the two, the code will report an error. Moreover, mixing the two often reduces the readability of the code, so it is also a good thing for JavaScript to restrict the mixing of the two at the language level.
var expertise = 'journalism' var journalism = { years: 5, interests: ['international', 'politics', 'internet'] } var person = { name: 'Sharon', age: 27, [expertise] // 這里會(huì)報(bào)語(yǔ)法錯(cuò)誤 }
Computable property names will make our code more concise when encountering the following scenarios:
The properties of a new object are referenced from another object:
var grocery = { id: 'bananas', name: 'Bananas', units: 6, price: 10, currency: 'USD' } var groceries = { [grocery.id]: grocery }
The attribute name of the object to be constructed comes from the function parameters. If we use ES5 to deal with this problem, we need to declare an object literal first, then dynamically add attributes, and then return the object. In the following example, we create a function that responds to Ajax requests. The function of this function is that when the request fails, the returned object has an attribute named
error
and a corresponding description. When the request succeeds, the returned object The object has a property namedsuccess
and a corresponding description.
// ES5 寫法 function getEnvelope(type, description) { var envelope = { data: {} } envelope[type] = description return envelope }
Using the calculated property name provided by ES6, a more concise implementation is as follows:
// ES6 寫法 function getEnvelope(type, description) { return { data: {}, [type]: description } }
The properties of object literals can be abbreviated, and the method is actually also possible.
Method definition
Let’s first look at how to define object methods traditionally. In the following code, we build an event generator in which on The
method is used to register events, and the emit
method is used to execute events:
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) }) } }
ES6’s object literal method abbreviation allows us to omit the function
key of the object method and the colon after it, the rewritten code is as follows:
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) }) } }
The arrow function in ES6 is famous, it has some special advantages (about this
), maybe you are like me , I have been using arrow functions for a long time, but there are some details that I have never understood before, such as several abbreviations of arrow functions and precautions for their use.
箭頭函數(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 為我們提供了一種寫匿名函數(shù)的新方法,即箭頭函數(shù)。箭頭函數(shù)不需要使用function
關(guān)鍵字,其參數(shù)和函數(shù)體之間以=>
相連接:
var example = (parameters) => { // function body }
盡管箭頭函數(shù)看起來(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)寫,接下來(lái)我們一起看看。
簡(jiǎn)寫的箭頭函數(shù)
完整的箭頭函數(shù)是這樣的:
var example = (parameters) => { // function body }
簡(jiǎn)寫1:
當(dāng)只有一個(gè)參數(shù)時(shí),我們可以省略箭頭函數(shù)參數(shù)兩側(cè)的括號(hào):
var double = value => { return value * 2 }
簡(jiǎn)寫2:
對(duì)只有單行表達(dá)式且,該表達(dá)式的值為返回值的箭頭函數(shù)來(lái)說(shuō),表征函數(shù)體的{}
,可以省略,return
關(guān)鍵字可以省略,會(huì)靜默返回該單一表達(dá)式的值。
var double = (value) => value * 2
簡(jiǎn)寫3:
上述兩種形式可以合并使用,而得到更加簡(jiǎn)潔的形式
var double = value => value * 2
現(xiàn)在,你肯定學(xué)會(huì)了箭頭函數(shù)的基本使用方法,接下來(lái)我們?cè)倏磶讉€(gè)使用示例。
簡(jiǎn)寫箭頭函數(shù)帶來(lái)的一些問(wèn)題
當(dāng)你的簡(jiǎn)寫箭頭函數(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)潔的方法修改子屬性的名稱:
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),情況也是類似的,如果你在左邊聲明了一個(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ì)算屬性名允許任何類似的表達(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)是類似的。區(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
,我們可以這樣寫,解構(gòu)幫助我們避免了很多中間變量的使用,也使得我們代碼的可讀性更高。
function getCoordinates() { return { x: 10, y: 22, z: -1, type: '3d' } } var { x, y } = getCoordinates()
通過(guò)使用默認(rèn)值,可以減少重復(fù),比如你想寫一個(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的寫法 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é)果。空格得以保留,多個(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ò),使用被稱為標(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)換所有用戶輸入的值為大寫(假設(shè)用戶只會(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)換輸入為大寫,那我們?cè)龠M(jìn)一步想想,如果提供合適的標(biāo)記模板函數(shù),使用標(biāo)記模板,我們還可以對(duì)模板中的表達(dá)式進(jìn)行各種過(guò)濾處理,比如有這么一個(gè)場(chǎng)景,假設(shè)表達(dá)式的值都來(lái)自用戶輸入,假設(shè)有一個(gè)名為sanitize
的庫(kù)可用于去除用戶輸入中的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)言之,如果你的代碼類似下面這樣,就會(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
聲明也具有類似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)槭切鹿δ芏皇褂茫珽S6語(yǔ)法被使用的前提是它可以顯著的提升我們代碼的可讀寫和可維護(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() } }
這類問(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
類似,而var
聲明使用函數(shù)作用域,可以重新指定值,可以在未聲明前調(diào)用,考慮到這些,推薦盡量不要使用var
聲明了。
【相關(guān)推薦:javascript視頻教程、編程視頻】
The above is the detailed content of What syntactic sugar does es6 have?. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

In ES6, you can use the reverse() method of the array object to achieve array reversal. This method is used to reverse the order of the elements in the array, putting the last element first and the first element last. The syntax "array.reverse()". The reverse() method will modify the original array. If you do not want to modify it, you need to use it with the expansion operator "...", and the syntax is "[...array].reverse()".

async is es7. async and await are new additions to ES7 and are solutions for asynchronous operations; async/await can be said to be syntactic sugar for co modules and generator functions, solving js asynchronous code with clearer semantics. As the name suggests, async means "asynchronous". Async is used to declare that a function is asynchronous; there is a strict rule between async and await. Both cannot be separated from each other, and await can only be written in async functions.

For browser compatibility. As a new specification for JS, ES6 adds a lot of new syntax and API. However, modern browsers do not have high support for the new features of ES6, so ES6 code needs to be converted to ES5 code. In the WeChat web developer tools, babel is used by default to convert the developer's ES6 syntax code into ES5 code that is well supported by all three terminals, helping developers solve development problems caused by different environments; only in the project Just configure and check the "ES6 to ES5" option.

Steps: 1. Convert the two arrays to set types respectively, with the syntax "newA=new Set(a);newB=new Set(b);"; 2. Use has() and filter() to find the difference set, with the syntax " new Set([...newA].filter(x =>!newB.has(x)))", the difference set elements will be included in a set collection and returned; 3. Use Array.from to convert the set into an array Type, syntax "Array.from(collection)".

In es5, you can use the for statement and indexOf() function to achieve array deduplication. The syntax "for(i=0;i<array length;i++){a=newArr.indexOf(arr[i]);if(a== -1){...}}". In es6, you can use the spread operator, Array.from() and Set to remove duplication; you need to first convert the array into a Set object to remove duplication, and then use the spread operator or the Array.from() function to convert the Set object back to an array. Just group.

In es6, the temporary dead zone is a syntax error, which refers to the let and const commands that make the block form a closed scope. Within a code block, before a variable is declared using the let/const command, the variable is unavailable and belongs to the variable's "dead zone" before the variable is declared; this is syntactically called a "temporary dead zone". ES6 stipulates that variable promotion does not occur in temporary dead zones and let and const statements, mainly to reduce runtime errors and prevent the variable from being used before it is declared, resulting in unexpected behavior.

No, require is the modular syntax of the CommonJS specification; and the modular syntax of the es6 specification is import. require is loaded at runtime, and import is loaded at compile time; require can be written anywhere in the code, import can only be written at the top of the file and cannot be used in conditional statements or function scopes; module attributes are introduced only when require is run. Therefore, the performance is relatively low. The properties of the module introduced during import compilation have slightly higher performance.

In ES6, you can use the length attribute of the array object to determine how many items there are in the array, that is, to get the number of elements in the array; this attribute can return the number of elements in the array, just use the "array.length" statement. Returns a value representing the number of elements of the array object, that is, the length value.
