ECMAScript 2015,也就是我們所知道的ES6,介紹了許多JavaScript的新特性。從那以後,每年都會增加一些新的特性。ES6及以後的擴充通常被稱為現代JavaScript,因為有着這些重要的變化。這篇文章主要探索的是ES6, ES7, ES8, ES9以及ES10的新特性。
什麼是ECMAScript
ECMAScript是一門腳本語言的标準。JavaScript是一門實作了ECMAScript标準的程式設計語言。ECMA國際是制定ES标準的協會。TC39是ECMA國際下的一個委員會,它決定JavaScript是如何工作的,以及應該有哪些新的特性。在2015年,JavaScript有了重大的變化,因為ES6釋出了,這意味着許多新特性增加了進來。(在釋出ES6之前的版本是2011年釋出的ES5.1)。從2015年開始,TC39每年都會增加一些新的特性。是以,每次新的版本不會再出現大量的特性,以及釋出時間上的巨大差距。
在英語中,ES是ECMAScript的簡稱。ES是一個标準,有點像一本專門講規則的書。腳本語言遵守ES的标準,而JavaScript就是一種遵循這些ES标準的腳本語言。
ES6 (2015)
變量提升
用
var
關鍵字聲明的變量會成為全局變量,或者成為函數的局部變量,但它沒有塊級作用域。使用
var
聲明的變量可以被提升,重新指派和重新聲明。
let
關鍵字允許變量有塊級作用域。使用
let
聲明的變量不會被提升,不能重新聲明,但可以被重新指派。
// ES5:
var name = 'Lisa'
// ES6:
let name = 'Lisa'
name = 'Bart' // 'Bart'
let name = 'Maggie' // 'Maggie'
常量聲明
const
關鍵字變量有塊級作用域。用
const
聲明的變量不能夠被提升、重新聲明,也不能重新指派,但卻是易變的。
// ES6:
const name = 'Lisa'
name = 'Bart' // 類型錯誤,不能對常量進行再次指派
const name = 'Maggie' // 文法錯誤,不能對常量進行再次聲明
用
const
聲明的變量是易變的。
// ES6:
const simpson = { name: 'Lisa' }
simpson.name = 'Bart' // 改變name屬性的值
simpson.address = '742 Evergreen Terrace' // 添加address屬性
console.log(simpson) // { name: 'Bart', address: '742 Evergreen Terrace' }
simpson = { name: 'Bart' } // 類型錯誤,不能對常量進行重新指派
箭頭函數
箭頭函數表達式寫起來比正常的函數表達式要簡短。箭頭函數不會綁定
this
,不應該将它作為方法,也不能将它作為構造器。
// ES5:
function sum(a, b) {
console.log(a + b)
}
const sum = function(a, b) {
console.log(a + b)
}
// ES6:
const sum = (a, b) => {
console.log(a + b)
}
模闆字元串
模闆字元串允許我們以更簡潔的方式在字元串中嵌入表達式。模闆字元串用反引号包起來。它們使用括号和美元符号表示占位符,就像這樣
${variableName}
。
let firstName = 'Lisa'
let lastName = 'Simpson'
// ES5:
'Hello, ' + firstName + ' ' + lastName + '.'
// 'Hello, Lisa Simpson.'
// ES6:
`Hello, ${firstName} ${lastName}.`
// 'Hello, Lisa Simpson.'
解構指派
解構指派允許我們從數組中取出元素,或者從對象中取出元素,指派給不同的變量。解構指派在 React.中被重度使用。
數組解構:
// ES5:
const one = numbers[0]
const two = numbers[1]
console.log(one) // 1
console.log(two) // 2
// ES6:
const [one, two, three, ...rest] = numbers
console.log(one) // 1
console.log(two) // 2
console.log(rest) // [3, 4, 5, 6, 7]
對象解構:
const lisa = {
age: 8,
school: 'Springfield Elementary',
}
ES5:
const age = lisa.age
const school = lisa.school
console.log(age) // 8
console.log(school) // 'Springfield Elementary'
ES6:
const { age, school } = lisa
console.log(age) // 8
console.log(school) // 'Springfield Elementary'
展開文法
展開文法允許像被操作對象像數組表達式或者字元串一樣展開疊代。
展開文法可以被用作展開一個數組:
ES6:
const family = ['Lisa', 'Bart', 'Maggie', 'Marge', 'Homer']
const pets = ["Santa's Little Helper", 'Snowball']
const theSimpsons = [...family, ...pets]
console.log(theSimpsons)
// ['Lisa', 'Bart', 'Maggie', 'Marge', 'Homer', "Santa's Little Helper", 'Snowball']
展開文法可以用在函數參數上:
ES6:
const array = [1, 2, 3]
const sumNumbers = (a, b, c) => a + b + c
console.log(sumNumbers(...array)) // 6
數組疊代
for...of
表達式建立了一個在可疊代對象(如數組、映射和集合)上的循環。
// ES5:
for (let i = 0; i < array.length; i++) {
console.log(array[i]) // 'Lisa', 'Marge', 'Maggie'
}
// ES6:
for (i of array) {
console.log(i) // 'Lisa', 'Marge', 'Maggie'
}
Promises
Promise是表示異步操作最終完成(或失敗)狀态以及結果值的對象。
// ES6:
function firstHello() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('First hello!')
resolve()
}, 5000)
})
}
function secondHello() {
console.log('Second hello!!')
}
console.log(firstHello().then(secondHello))
// Promise pending <-- promise pends for 5 seconds
// First hello!
// Second hello!!
ES7 (2016)
Array.prototype.includes()
The
includes()
method returns
true
if an array includes a certain specified value, and returns
false
if it doesn’t.
如果一個數組包含指定的值,則
includes()
方法傳回
ture
,如果不包含則傳回
false
。
// ES7:
const simpsons = ['Lisa', 'Bart', 'Maggie', 'Marge', 'Homer']
simpsons.includes('Lisa') // returns true
simpsons.includes('Ned') // returns false
指數操作符
**
表達式傳回将第一個操作數作為底數,第二個操作數作為幂的結果。
指數運算符是右聯式的。比如,
2 ** 3 ** 3
等于
2 ** (3 ** 3)
// ES6:
Math.pow(2, 3) // 8
// ES7:
2 ** 3 // 8
2 ** (3 ** 3) // 134217728
ES 8 (2017)
Object.values()
Object,values()
将一個對象作為一個參數,傳回一個由對象的值的集合組成的數組。傳回值的順序跟
for...in
循環調用的順序一樣。
// ES8:
const character = {
name: 'Lisa',
age: 8,
school: 'Springfield Elementary',
}
console.log(Object.values(character))
// ['Lisa', 8, 'Springfield Elementary']
Object.entries()
Object.entries()
将一個對象作為參數,傳回值是一個由鍵-值對數組組成的二維數組。傳回值的順序就像
for...in
循環調用的順序一樣
// ES8:
const character = {
name: 'Lisa',
age: 8,
school: 'Springfield Elementary',
}
console.log(Object.entries(character))
// [[name: 'Lisa'], [age: 8], [school: 'Springfield Elementary']]
String.prototype.padEnd()
padEnd()
的作用是用另一個字元串填充字元串的末尾,一直到符合字元串長度的要求。如果有必要的話,用于填充的字元串将會被重複多次。
// ES8:
'The'.padEnd(10) // 'The '
'The'.padEnd(10, ' Simpsons') // 'The Simpso'
'The'.padEnd(12, ' Simpsons') // 'The Simpsons'
'The'.padEnd(15, ' Simpsons') // 'The Simpsons Si'
String.prototype.padStart()
padEnd()
的作用是用另一個字元串填充字元串的頭部,一直到符合字元串長度的要求。如果有必要的話,用于填充的字元串将會被重複多次。
// ES8:
'Simpsons'.padStart(10) // ' Simpsons'
'Simpsons'.padStart(10, 'The ') // 'ThSimpsons'
'Simpsons'.padStart(12, 'The ') // 'The Simpsons'
'Simpsons'.padStart(15, 'The ') // 'The TheSimpsons'
末尾逗号
末尾逗号是指在最後一個元素末尾的逗号。當你需要增加新的一行而不需要改變之前最後一行的時候它是很有用的,因為它已經有一個逗号了。末尾的逗号也會讓版本控制的差異更清晰。
// ES8:
const character = {
name: 'Lisa',
age: 8,
school: 'Springfield Elementary',
}
Async functions (async/await)
Async/await
是promises的進階文法糖。它讓異步代碼更加閱讀和編寫。一個
async
函數知道它可能會期待一個
await
關鍵字來調用異步代碼。
async
函數也知道它應該傳回一個
promise
而不是直接傳回一個值。函數前面的
async
關鍵字會把一個普通函數變為一個異步函數。
await
關鍵字隻能在
async
函數内部使用。
await
關鍵字用來暫停一行
promise
代碼的執行,直到這個
promise
變為
fufilled
(就緒)狀态。
異步的代碼看起來就像這樣:
async function hello() {
return (greeting = await Promise.resolve('Hello'))
}
下面這個例子說明了
async/await
的能力:
// ES8:
function delayedHello() {
return new Promise(res => {
setTimeout(() => res(console.log('A delayed hello!')), 5000)
})
}
async function logHello() {
await delayedHello()
}
console.log('Before logHello')
logHello()
console.log('After logHello')
// Before logHello
// After logHello
// A delayed hello! <-- logs after 5 seconds has passed
ES9 (2018)
Promise.prototype.finally()
finally()
方法都會被執行,無論
Promise
是
resolved
還是
rejected
的狀态。
// ES9:
const hello = () => {
console.log('hello')
}
try {
hello()
} catch (error) {
console.log(error)
} finally {
console.log('all done!')
}
// hello
// all done! <-- logs when promise is resolved
try {
functionDoesNotExist()
} catch (error) {
console.log(error)
} finally {
console.log('all done!')
}
// ReferenceError: functionDoesNotExist is not defined
// all done <-- logs when promise is rejected
ES10 (2019)
Array.flat()
flat()
方法會建立一個新的數組,将子數組元素都将被加入進來。它有一個可選參數,
depth
,預設值是1。
depth
指定了嵌套數組應該被扁平化的深度。
// ES10:
const array = [1, 2, 3, [4, 5]]
console.log(array.flat())
// [1, 2, 3, 4, 5]
const array2 = [1, 2, [3, [4, 5]]]
console.log(array2.flat())
// [1, 2, 3, [4, 5]]
console.log(array2.flat(2))
// [1, 2, 3, 4, 5]
Array.flatMap()
flat.map()
方法使用map函數映射每個元素,然後将結果進行扁平化并放入一個數組中。這相當于向使用
map()
函數,再調用深度為1的
flat()
方法。
// ES10 map() then flat():
const array = [1, 2, 3, 4, 5]
console.log(array.map(x => [x * 2]))
// [[2], [4], [6], [8], [10]]
console.log(array.map(x => [x * 2]).flat())
// [2, 4, 6, 8, 10]
// ES10 flatMap():
const array = [1, 2, 3, 4, 5]
console.log(array.flatMap(x => [x * 2]))
// [2, 4, 6, 8, 10]
Object.fromEntries()
Object.fromEntries()
方法是将一個
key-value
對的清單轉為一個對象。
// ES10:
const lisaArray = [
['grade', 2],
['school', 'Springfield Elementary'],
]
console.log(Object.fromEntries(lisaArray))
// {grade: 2, school: "Springfield Elementary"}
const lisaMap = new Map([
['grade', 2],
['school', 'Springfield Elementary'],
])
console.log(Object.fromEntries(lisaMap))
// {grade: 2, school: "Springfield Elementary"}
TrimStart()
trimStart()
方法用于移除一個字元串頭部的空格。
// ES10:
const simpsons = ' The Simpsons '
console.log(simpsons.trimStart())
// 'The Simpsons '
TrimEnd()
trimEnd()
方法用于移除一個字元串尾部的空格。
// ES10:
const simpsons = ' The Simpsons '
console.log(simpsons.trimEnd())
// ' The Simpsons'
可選的catch參數
在
try...catch
中,錯誤參數現在是可選的。
// ES10:
try {
// do something
} catch (err) {
// handle error
}
// ES10:
try {
// do something
} catch {
// handle error
}
總結
這就是我們的收獲!現在你知道了如何使用許多常用的ES6+的javascript新特性了。測試一下你的JavaScript知識吧JavaScript questions by Lydia Hallie.