2022年了,這些ES7-ES12的知識(shí)點(diǎn)你都掌握了嘛?
點(diǎn)擊上方?前端瓶子君,關(guān)注公眾號(hào)
回復(fù)算法,加入前端編程面試算法每日一題群
前言
聽說現(xiàn)在大家在工作中ES6語法都已經(jīng)用的爐火純青了,那ES7-ES12的新特性你現(xiàn)在都用上了嘛?很多的新特性在開發(fā)中還是很實(shí)用的,也解決了很多js存在的問題。自己熬夜爆肝一個(gè)周末,總結(jié)出了ES7-ES12的語法,希望對(duì)你能有一定的幫助。最后,了解真相,你才能獲得真正的自由!
ES2016(ES7)
Array.prototype.includes()
includes()?方法用來判斷一個(gè)數(shù)組是否包含一個(gè)指定的值,如果包含則返回?true,否則返回?false。
語法
arr.includes(valueToFind[,?fromIndex])
復(fù)制代碼
valueToFind,需要查找的元素值。
fromIndex?可選 從fromIndex?索引處開始查找?valueToFind。如果為負(fù)值(即從末尾開始往前跳?fromIndex?的絕對(duì)值個(gè)索引,然后往后搜尋)。默認(rèn)為 0。
示例
const?arr?=?['es6',?'es7',?'es8']
console.log(arr.includes('es7'))?//?true
console.log(arr.includes('es7',?1))?//?true
console.log(arr.includes('es7',?2))?//?false
console.log(arr.includes("es7",?-1));?//?fsle
console.log(arr.includes("es7",?-2));?//?true
復(fù)制代碼
注意點(diǎn)
使用 includes()查找字符串是區(qū)分大小寫的。
const?arr?=?["es6",?"es7",?"es8",?"a"];
console.log(arr.includes("A"));?//?false
復(fù)制代碼
使用 includes()只能判斷簡(jiǎn)單類型的數(shù)據(jù),對(duì)于復(fù)雜類型的數(shù)據(jù),比如對(duì)象類型的數(shù)組,二維數(shù)組,這些是無法判斷的.
const?arr?=?['es6',?['es7',?'es8'],?'es9',{name:"jimmy"}]
console.log(arr.includes(["es7",?"es8"]));?//?false
console.log(arr.includes({name:"jimmy"}));?//?false
復(fù)制代碼
能識(shí)別NaN,indexOf是不能識(shí)別NaN的
const?arr?=?['es6',?'es7',?NaN,?'es8']
console.log(arr.includes(NaN))?//?true
console.log(arr.indexOf(NaN))?//?-1
復(fù)制代碼
最后,如果只想知道某個(gè)值是否在數(shù)組中存在,而并不關(guān)心它的索引位置,建議使用includes(),如果想獲取一個(gè)值在數(shù)組中的位置,那么使用indexOf方法。
冪運(yùn)算符 **
比如我們想求2的10次方。
自己寫函數(shù)實(shí)現(xiàn)
function?pow(x,?y)?{
????let?result?=?1
????for?(let?i?=?0;?i?????????result?*=?x
????}
????return?result
}
console.log(pow(2,?10))?//?1024
復(fù)制代碼
Math.pow()
console.log(Math.pow(2,?10));?//?1024
復(fù)制代碼
冪運(yùn)算符 **
console.log(2?**?10);?//?1024
復(fù)制代碼
基本求冪
2?**?3???//?8
3?**?2???//?9
3?**?2.5?//?15.588457268119896
10?**?-1?//?0.1
NaN?**?2?//?NaN
復(fù)制代碼
注意
冪運(yùn)算符的兩個(gè)*號(hào)之間不能出現(xiàn)空格,否則語法會(huì)報(bào)錯(cuò)。
ES2017(ES8)
Object.values()
Object.values 方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。
const?obj?=?{
??name:?"jimmy",
??age:?18,
??height:?188,
};
console.log(Object.values(obj));?//?[?'jimmy',?18,?188?]
復(fù)制代碼
Object.entries()
Object.entries() 方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷屬性的鍵值對(duì)數(shù)組。
const?obj?=?{
??name:?"jimmy",
??age:?18,
??height:?188,
};
console.log(Object.entries(obj));?//?[?[?'name',?'jimmy'?],?[?'age',?18?],?[?'height',?188?]?]
console.log(Object.entries([1,?2,?3]));?//?[?[?'0',?1?],?[?'1',?2?],?[?'2',?3?]?]
復(fù)制代碼
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors() ?方法用來獲取一個(gè)對(duì)象的所有自身屬性的描述符。
const?obj?=?{
??name:?"jimmy",
??age:?18,
};
const?desc?=?Object.getOwnPropertyDescriptors(obj);
console.log(desc);??
//?打印結(jié)果
{
??name:?{
????value:?'jimmy',
????writable:?true,
????enumerable:?true,
????configurable:?true
??},
??age:?{?
???value:?18,?
???writable:?true,
???enumerable:?true,?
???configurable:?true?
??}
}
復(fù)制代碼
上面打印結(jié)果中的
value表示當(dāng)前對(duì)象的默認(rèn)值writable表示對(duì)象屬性是否可以修改enumerable表示當(dāng)前這個(gè)屬性是否可以出現(xiàn)在對(duì)象的枚舉屬性中configurable表示當(dāng)前對(duì)象的屬性能否用delete刪除
那這些對(duì)象的屬性我們?cè)趺丛O(shè)置和修改他們呢,我們可以使用es5的 Object.defineProperty()
const?obj?=?{};
Object.defineProperty(obj,?"name",?{
??value:?"jimmy",
??writable:?true,
??configurable:?true,
??enumerable:?true,
});
Object.defineProperty(obj,?"age",?{
??value:?34,
??writable:?true,
??configurable:?true,
??enumerable:?true,
});
console.log(obj);?//?{?name:?'jimmy',?age:?34?}
復(fù)制代碼
接下來我們演示下,一些屬性設(shè)置為false的情況
const?obj?=?{};
Object.defineProperty(obj,?"name",?{
??value:?"jimmy",
??writable:?false,
??configurable:?false,
??enumerable:?true,
});
console.log(obj);?//?{?name:?'jimmy'?}
obj.name?=?"chimmy";
console.log(obj);?//?{?name:?'jimmy'?}
delete?obj.name
console.log(obj);?//?{?name:?'jimmy'?}
復(fù)制代碼
我們可以看到設(shè)置 writable: false和configurable: false,為false時(shí),對(duì)象的name對(duì)象的值不能改變和不能被刪除,打印出來還是原來的對(duì)象。
設(shè)置enumerable為false時(shí)
const?obj?=?{};
Object.defineProperty(obj,?"name",?{
??value:?"jimmy",
??writable:?true,
??configurable:?true,
??enumerable:?false,
});
console.log(obj);?//?{?}
for?(let?key?in?obj)?{
??console.log(key);?//?""
}
復(fù)制代碼
當(dāng)設(shè)置enumerable: false時(shí),表示對(duì)象的屬性不可被枚舉,這時(shí)打印對(duì)象為空,遍歷對(duì)象的鍵也為空。
String.prototype.padStart
把指定字符串填充到字符串頭部,返回新字符串。
語法
str.padStart(targetLength [, padString])
targetLength
當(dāng)前字符串需要填充到的目標(biāo)長(zhǎng)度。如果這個(gè)數(shù)值小于當(dāng)前字符串的長(zhǎng)度,則返回當(dāng)前字符串本身。
padString?可選
填充字符串。如果字符串太長(zhǎng),使填充后的字符串長(zhǎng)度超過了目標(biāo)長(zhǎng)度,則只保留最左側(cè)的部分,其他部分會(huì)被截?cái)?。此參?shù)的默認(rèn)值為 " "
示例
'abc'.padStart(10);?????????//?"???????abc"
'abc'.padStart(10,?"foo");??//?"foofoofabc"
'abc'.padStart(6,"123465");?//?"123abc"
'abc'.padStart(8,?"0");?????//?"00000abc"
'abc'.padStart(1);??????????//?"abc"
復(fù)制代碼
應(yīng)用場(chǎng)景
日期格式化:yyyy-mm-dd的格式:
const?now?=?new?Date()
const?year?=?now.getFullYear()
//?月份和日期?如果是一位前面給它填充一個(gè)0
const?month?=?(now.getMonth()?+?1).toString().padStart(2,?'0')
const?day?=?(now.getDate()).toString().padStart(2,?'0')
console.log(year,?month,?day)
console.log(?`${year}-${month}-${day}`?)?//輸入今天的日期?2021-12-31
復(fù)制代碼
數(shù)字替換(手機(jī)號(hào),銀行卡號(hào)等)
const?tel?=?'18781268679'
const?newTel?=?tel.slice(-4).padStart(tel.length,?'*')
console.log(newTel)?//?*******5678
復(fù)制代碼
String.prototype.padEnd
把指定字符串填充到字符串尾部,返回新字符串。
語法同上
示例
'abc'.padEnd(10);??????????//?"abc???????"
'abc'.padEnd(10,?"foo");???//?"abcfoofoof"
'abc'.padEnd(6,?"123456");?//?"abc123"
'abc'.padEnd(1);???????????//?"abc"
復(fù)制代碼
應(yīng)用場(chǎng)景
在JS前端我們處理時(shí)間戳的時(shí)候單位是ms毫秒,但是,后端同學(xué)返回的時(shí)間戳則不一樣是毫秒,可能只有10位,以s秒為單位。所以,我們?cè)谇岸颂幚磉@個(gè)時(shí)間戳的時(shí)候,保險(xiǎn)起見,要先做一個(gè)13位的補(bǔ)全,保證單位是毫秒。
//?偽代碼
console.log(new?Date().getTime())?//?時(shí)間戳?13位的
timestamp?=?+String(timestamp).padEnd(13,?'0')
復(fù)制代碼
尾逗號(hào) Trailing commas
ES8 允許函數(shù)的最后一個(gè)參數(shù)有尾逗號(hào)(Trailing comma)。此前,函數(shù)定義和調(diào)用時(shí),都不允許最后一個(gè)參數(shù)后面出現(xiàn)逗號(hào)。
function?clownsEverywhere(
????param1,
????param2
)?{
????/*?...?*/
}
clownsEverywhere(
????'foo',
????'bar'
)
復(fù)制代碼
上面代碼中,如果在param2或bar后面加一個(gè)逗號(hào),就會(huì)報(bào)錯(cuò)。
如果像上面這樣,將參數(shù)寫成多行(即每個(gè)參數(shù)占據(jù)一行),以后修改代碼的時(shí)候,想為函數(shù)clownsEverywhere添加第三個(gè)參數(shù),或者調(diào)整參數(shù)的次序,就勢(shì)必要在原來最后一個(gè)參數(shù)后面添加一個(gè)逗號(hào)。這對(duì)于版本管理系統(tǒng)來說,就會(huì)顯示添加逗號(hào)的那一行也發(fā)生了變動(dòng)。這看上去有點(diǎn)冗余,因此新的語法允許定義和調(diào)用時(shí),尾部直接可以加上一個(gè)逗號(hào)。
function?clownsEverywhere(
????param1,
????param2,
)?{
????/*?...?*/
}
clownsEverywhere(
????'foo',
????'bar',
)
復(fù)制代碼
這樣的規(guī)定也使得,函數(shù)參數(shù)與數(shù)組和對(duì)象的尾逗號(hào)規(guī)則,保持一致了。
async/await
介紹
我們都知道使用 Promise 能很好地解決回調(diào)地獄的問題,但如果處理流程比較復(fù)雜的話,那么整段代碼將充斥著 then,語義化不明顯,代碼不能很好地表示執(zhí)行流程,那有沒有比 Promise 更優(yōu)雅的異步方式呢?那就是async/await!我們一起來揭開它神秘的面撒吧!
前面添加了async的函數(shù)在執(zhí)行后都會(huì)自動(dòng)返回一個(gè)Promise對(duì)象:
function?foo()?{
????return?'jimmy'
}
console.log(foo())?//?'jimmy'
復(fù)制代碼
添加async后
async?function?foo()?{
????return?'jimmy'?//?Promise.resolve('jimmy')
}
console.log(foo())?//?Promise
foo()
復(fù)制代碼
async函數(shù)中使用await,那么await這里的代碼就會(huì)變成同步的了,意思就是說只有等await后面的Promise執(zhí)行完成得到結(jié)果才會(huì)繼續(xù)下去,await就是等待。請(qǐng)看下面的示例:
function?timeout()?{
????return?new?Promise(resolve?=>?{
????????setTimeout(()?=>?{
????????????console.log(1)
????????????resolve()
????????},?1000)
????})
}
//?不加async和await是2、1???加了是1、2
async?function?foo()?{
????await?timeout()?
????console.log(2)
}
foo()
復(fù)制代碼
使用場(chǎng)景
假如有這樣一個(gè)使用場(chǎng)景:需要先請(qǐng)求 a 鏈接,等返回信息之后,再請(qǐng)求 b 鏈接的另外一個(gè)資源。下面代碼展示的是使用 fetch 來實(shí)現(xiàn)這樣的需求,fetch 被定義在 window 對(duì)象中,它返回的是一個(gè) Promise 對(duì)象。
fetch('https://blog.csdn.net/')
??.then(response?=>?{
????console.log(response)
????return?fetch('https://juejin.im/')
??})
??.then(response?=>?{
????console.log(response)
??})
??.catch(error?=>?{
????console.log(error)
??})
復(fù)制代碼
雖然上述代碼可以實(shí)現(xiàn)這個(gè)需求,但語義化不明顯,代碼不能很好地表示執(zhí)行流程?;谶@個(gè)原因,ES8 引入了 async/await,這是 JavaScript 異步編程的一個(gè)重大改進(jìn),提供了在不阻塞主線程的情況下使用同步代碼實(shí)現(xiàn)異步訪問資源的能力,并且使得代碼邏輯更加清晰。
async?function?foo?()?{
??try?{
????let?response1?=?await?fetch('https://blog.csdn.net/')
????console.log(response1)
????let?response2?=?await?fetch('https://juejin.im/')
????console.log(response2)
??}?catch?(err)?{
????console.error(err)
??}
}
foo()
復(fù)制代碼
通過上面代碼,你會(huì)發(fā)現(xiàn)整個(gè)異步處理的邏輯都是使用同步代碼的方式來實(shí)現(xiàn)的,而且還支持 try catch 來捕獲異常,這感覺就在寫同步代碼,所以是非常符合人的線性思維的。
注意點(diǎn)
await 只能在 async 標(biāo)記的函數(shù)內(nèi)部使用,單獨(dú)使用會(huì)觸發(fā) Syntax error。
await后面需要跟異步操作,不然就沒有意義,而且await后面的Promise對(duì)象不必寫then,因?yàn)閍wait的作用之一就是獲取后面Promise對(duì)象成功狀態(tài)傳遞出來的參數(shù)。
async/await的缺陷
了解Async/await是非常有用的,但還有一些缺點(diǎn)需要考慮。
Async/await?讓你的代碼看起來是同步的,在某種程度上,也使得它的行為更加地同步。?await?關(guān)鍵字會(huì)阻塞其后的代碼,直到promise完成,就像執(zhí)行同步操作一樣。它確實(shí)可以允許其他任務(wù)在此期間繼續(xù)運(yùn)行,但您自己的代碼被阻塞。
這意味著您的代碼可能會(huì)因?yàn)榇罅?code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">await的promises相繼發(fā)生而變慢。每個(gè)await都會(huì)等待前一個(gè)完成,而你實(shí)際想要的是所有的這些promises同時(shí)開始處理(就像我們沒有使用async/await時(shí)那樣)。
有一種模式可以緩解這個(gè)問題——通過將?Promise?對(duì)象存儲(chǔ)在變量中來同時(shí)開始它們,然后等待它們?nèi)繄?zhí)行完畢。如果想更加深入的了解,請(qǐng)參考 MDN[1]
ES2018(ES9)
Object Rest & Spread
在 ES9 新增 Object 的 Rest & Spread 方法,直接看下示例:
const?input?=?{
??a:?1,
??b:?2,
??c:?3,
}
const?output?=?{
??...input,
??c:?4
}
console.log(output)?//?{a:?1,?b:?2,?c:?4}
復(fù)制代碼
這塊代碼展示了 spread 語法,可以把 input 對(duì)象的數(shù)據(jù)都拓展到 output 對(duì)象,這個(gè)功能很實(shí)用。需要注意的是,如果存在相同的屬性名,只有最后一個(gè)會(huì)生效。
注意點(diǎn)
const?obj?=?{?x:?{?y:?10?}?};
const?copy1?=?{?...obj?};
const?copy2?=?{?...obj?};
obj.x.y?=?"jimmy";
console.log(copy1,?copy2);?//?x:?{y:?"jimmy"}?x:?{y:?"jimmy"}
console.log(copy1.x?===?copy2.x);?//?→?true
復(fù)制代碼
如果屬性的值是一個(gè)對(duì)象的話,該對(duì)象的引用會(huì)被拷貝,而不是生成一個(gè)新的對(duì)象。
我們?cè)賮砜聪?Object rest 的示例:
const?input?=?{
??a:?1,
??b:?2,
??c:?3
}
let?{?a,?...rest?}?=?input
console.log(a,?rest)?//?1?{b:?2,?c:?3}
復(fù)制代碼
當(dāng)對(duì)象 key-value 不確定的時(shí)候,把必選的 key 賦值給變量,用一個(gè)變量收斂其他可選的 key 數(shù)據(jù),這在之前是做不到的。注意,rest 屬性必須始終出現(xiàn)在對(duì)象的末尾,否則將拋出錯(cuò)誤。
for await of
異步迭代器(for-await-of):循環(huán)等待每個(gè)Promise對(duì)象變?yōu)閞esolved狀態(tài)才進(jìn)入下一步。
我們知道 for...of 是同步運(yùn)行的,看如下代碼
function?TimeOut(time){
????return?new?Promise(function(resolve,?reject)?{
????????setTimeout(function()?{
????????????resolve(time)
????????},?time)
????})
}
async?function?test()?{
????let?arr?=?[TimeOut(2000),?TimeOut(1000),?TimeOut(3000)]
????for?(let?item?of?arr)?{??
?????console.log(Date.now(),item.then(console.log))
????}
}
test()
復(fù)制代碼
上面打印結(jié)果如下圖

上述代碼證實(shí)了 for of 方法不能遍歷異步迭代器,得到的結(jié)果并不是我們所期待的,于是 for await of 就粉墨登場(chǎng)啦!
ES9 中可以用 for...await...of 的語法來操作
function?TimeOut(time)?{
????return?new?Promise(function(resolve,?reject)?{
????????setTimeout(function()?{
????????????resolve(time)
????????},?time)
????})
}
async?function?test()?{
????let?arr?=?[TimeOut(2000),?TimeOut(1000),?TimeOut(3000)]
????for?await?(let?item?of?arr)?{
????????console.log(Date.now(),?item)
????}
}
test()
//?1560092345730?2000
//?1560092345730?1000
//?1560092346336?3000
復(fù)制代碼
for await of 環(huán)等待每個(gè)Promise對(duì)象變?yōu)閞esolved狀態(tài)才進(jìn)入下一步。所有打印的結(jié)果為 2000,1000,3000
Promise.prototype.finally()
Promise.prototype.finally() 方法返回一個(gè)Promise,在promise執(zhí)行結(jié)束時(shí),無論結(jié)果是fulfilled或者是rejected,在執(zhí)行then()和catch()后,都會(huì)執(zhí)行finally指定的回調(diào)函數(shù)。這為指定執(zhí)行完promise后,無論結(jié)果是fulfilled還是rejected都需要執(zhí)行的代碼提供了一種方式,避免同樣的語句需要在then()和catch()中各寫一次的情況。
示例
new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
????????resolve('success')
????????//?reject('fail')
????},?1000)
}).then(res?=>?{
????console.log(res)
}).catch(err?=>?{
????console.log(err)
}).finally(()?=>?{
????console.log('finally')
})
復(fù)制代碼
使用場(chǎng)景
loading關(guān)閉
需要每次發(fā)送請(qǐng)求,都會(huì)有l(wèi)oading提示,請(qǐng)求發(fā)送完畢,就需要關(guān)閉loading提示框,不然界面就無法被點(diǎn)擊。不管請(qǐng)求成功或是失敗,這個(gè)loading都需要關(guān)閉掉,這時(shí)把關(guān)閉loading的代碼寫在finally里再合適不過了
String 擴(kuò)展
放松對(duì)標(biāo)簽?zāi)0謇镒址D(zhuǎn)義的限制, 遇到不合法的字符串轉(zhuǎn)義會(huì)返回undefined,并且從raw上可獲取原字符串。
下面是一個(gè)es6 的標(biāo)簽?zāi)0?如果對(duì)這個(gè)語法感到陌生,請(qǐng)參考 標(biāo)簽?zāi)0?/span>[2]
const?foo?=?(a,?b,?c)?=>?{
????console.log(a)
????console.log(b)
????console.log(c)
}
const?name?=?'jimmy'
const?age?=?18
foo?`這是${name},他的年齡是${age}歲`?
復(fù)制代碼
參數(shù)打印如下:
ES9開始,模板字符串允許嵌套支持常見轉(zhuǎn)義序列,移除對(duì)ECMAScript在帶標(biāo)簽的模版字符串中轉(zhuǎn)義序列的語法限制。
function?foo(a,?b,?c)?{
????console.log(a,?b,?c)
}
//?在標(biāo)簽函數(shù)中使用?
//?unicode字符\u{61}?對(duì)應(yīng)的值為?a
//?unicode字符\u{62}?對(duì)應(yīng)的值為?b
//?\unicode?是一個(gè)無效的unicode字符
foo?`\u{61}?and?\u{62}`?
foo?`\u{61}?and?\unicode`??
復(fù)制代碼

注意點(diǎn)
在模板字符串中,如果輸入無效的unicode字符,還是會(huì)報(bào)錯(cuò)。只有在便簽?zāi)0逯?從es9開始才不會(huì)報(bào)錯(cuò)。
?let?string?=?`\u{61}?and?\unicode`;
?console.log(string);?//?Uncaught?SyntaxError:?Invalid?Unicode?escape?sequence
復(fù)制代碼
ES2019(ES10)
Object.fromEntries()
方法 Object.fromEntries() 把鍵值對(duì)列表轉(zhuǎn)換為一個(gè)對(duì)象,這個(gè)方法是和 Object.entries() 相對(duì)的。
Object.fromEntries([
????['foo',?1],
????['bar',?2]
])
//?{foo:?1,?bar:?2}
復(fù)制代碼
案例1:Object 轉(zhuǎn)換操作
const?obj?=?{
????name:?'jimmy',
????age:?18
}
const?entries?=?Object.entries(obj)
console.log(entries)
//?[Array(2),?Array(2)]
//?ES10
const?fromEntries?=?Object.fromEntries(entries)
console.log(fromEntries)
//?{name:?"jimmy",?age:?18}
復(fù)制代碼
案例2:Map 轉(zhuǎn) Object
const?map?=?new?Map()
map.set('name',?'jimmy')
map.set('age',?18)
console.log(map)?//?{'name'?=>?'jimmy',?'age'?=>?18}
const?obj?=?Object.fromEntries(map)
console.log(obj)
//?{name:?"jimmy",?age:?18}
復(fù)制代碼
案例3:過濾
course表示所有課程,想請(qǐng)求課程分?jǐn)?shù)大于80的課程組成的對(duì)象:
const?course?=?{
????math:?80,
????english:?85,
????chinese:?90
}
const?res?=?Object.entries(course).filter(([key,?val])?=>?val?>?80)
console.log(res)?//?[?[?'english',?85?],?[?'chinese',?90?]?]
console.log(Object.fromEntries(res))?//?{?english:?85,?chinese:?90?}
復(fù)制代碼
案例4:url的search參數(shù)轉(zhuǎn)換
//?let?url?=?"https://www.baidu.com?name=jimmy&age=18&height=1.88"
//?queryString?為?window.location.search
const?queryString?=?"?name=jimmy&age=18&height=1.88";
const?queryParams?=?new?URLSearchParams(queryString);
const?paramObj?=?Object.fromEntries(queryParams);
console.log(paramObj);?//?{?name:?'jimmy',?age:?'18',?height:?'1.88'?}
復(fù)制代碼
Array.prototype.flat()
語法
let?newArray?=?arr.flat([depth])
復(fù)制代碼
depth?可選
指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1。
示例
flat() ?方法會(huì)按照一個(gè)可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個(gè)新數(shù)組返回。
const?arr1?=?[0,?1,?2,?[3,?4]];
console.log(arr1.flat());??//??[0,?1,?2,?3,?4]
const?arr2?=?[0,?1,?2,?[[[3,?4]]]];
console.log(arr2.flat(2));??//??[0,?1,?2,?[3,?4]]
//使用?Infinity,可展開任意深度的嵌套數(shù)組
var?arr4?=?[1,?2,?[3,?4,?[5,?6,?[7,?8,?[9,?10]]]]];
arr4.flat(Infinity);?//?[1,?2,?3,?4,?5,?6,?7,?8,?9,?10]
//?`flat()`?方法會(huì)移除數(shù)組中的空項(xiàng):
var?arr5?=?[1,?2,?,?4,?5];
arr5.flat();?//?[1,?2,?4,?5]
復(fù)制代碼
Array.prototype.flatMap()
flatMap() 方法首先使用映射函數(shù)映射每個(gè)元素,然后將結(jié)果壓縮成一個(gè)新數(shù)組。從方法的名字上也可以看出來它包含兩部分功能一個(gè)是 map,一個(gè)是 flat(深度為1)。
語法
var?new_array?=?arr.flatMap(function?callback(currentValue[,?index[,?array]])?{
????//?返回新數(shù)組的元素
}[,?thisArg])
復(fù)制代碼
callback
可以生成一個(gè)新數(shù)組中的元素的函數(shù),可以傳入三個(gè)參數(shù):
currentValue
當(dāng)前正在數(shù)組中處理的元素
index
可選 數(shù)組中正在處理的當(dāng)前元素的索引。
array
可選 被調(diào)用的?map?數(shù)組
thisArg可選
執(zhí)行?callback?函數(shù)時(shí)?使用的this?值。
示例
const?numbers?=?[1,?2,?3]
numbers.map(x?=>?[x?*?2])?//?[[2],?[4],?[6]]
numbers.flatMap(x?=>?[x?*?2])?//?[2,?4,?6]
復(fù)制代碼
這個(gè)示例可以簡(jiǎn)單對(duì)比下 map 和 flatMap 的區(qū)別。當(dāng)然還可以看下下面的示例:
let?arr?=?['今天天氣不錯(cuò)',?'',?'早上好']
arr.map(s?=>?s.split(''))
//?[["今",?"天",?"天",?"氣",?"不",?"錯(cuò)"],[""],["早",?"上",?"好"]]
arr.flatMap(s?=>?s.split(''))
//?["今",?"天",?"天",?"氣",?"不",?"錯(cuò)",?"",?"早",?"上",?"好"]
復(fù)制代碼
flatMap?方法與?map?方法和深度depth為1的?flat?幾乎相同.
String.prototype.trimStart()
trimStart() 方法從字符串的開頭刪除空格,trimLeft()是此方法的別名。
let?str?=?'???foo??'
console.log(str.length)?//?8
str?=?str.trimStart()?//?或str.trimLeft()
console.log(str.length)?//?5
復(fù)制代碼
String.prototype.trimEnd()
trimEnd() 方法從一個(gè)字符串的右端移除空白字符,trimRight 是 trimEnd 的別名。
let?str?=?'???foo??'
console.log(str.length)?//?8
str?=?str.trimEnd()?//?或str.trimRight()
console.log(str.length)?//?6
復(fù)制代碼
可選的Catch Binding
在 ES10 之前我們都是這樣捕獲異常的:
try?{
????//?tryCode
}?catch?(err)?{
????//?catchCode
}
復(fù)制代碼
在這里 err 是必須的參數(shù),在 ES10 可以省略這個(gè)參數(shù):
try?{
????console.log('Foobar')
}?catch?{
????console.error('Bar')
}
復(fù)制代碼
應(yīng)用
驗(yàn)證參數(shù)是否為json格式
這個(gè)需求我們只需要返回true或false,并不關(guān)心catch的參數(shù)。
const?validJSON?=?json?=>?{
????try?{
????????JSON.parse(json)
????????return?true
????}?catch?{
????????return?false
????}
}
復(fù)制代碼
Symbol.prototype.description
我們知道,Symbol 的描述只被存儲(chǔ)在內(nèi)部的?Description?,沒有直接對(duì)外暴露,我們只有調(diào)用 Symbol 的 toString() 時(shí)才可以讀取這個(gè)屬性:
const?name?=?Symbol('es')
console.log(name.toString())?//?Symbol(es)
console.log(name)?//?Symbol(es)
console.log(name?===?'Symbol(es)')?//?false
console.log(name.toString()?===?'Symbol(es)')?//?true
復(fù)制代碼
現(xiàn)在可以通過 description 方法獲取 Symbol 的描述:
const?name?=?Symbol('es')
console.log(name.description)?//?es
name.description?=?"es2"?//?只讀屬性?并不能修改描述符
console.log(name.description?===?'es')?//?true
//?如果沒有描述符?輸入undefined
const?s2?=?Symbol()
console.log(s2.description)?//?undefined
復(fù)制代碼
JSON.stringify() 增強(qiáng)能力
JSON.stringify 在 ES10 修復(fù)了對(duì)于一些超出范圍的 Unicode 展示錯(cuò)誤的問題。因?yàn)?JSON 都是被編碼成 UTF-8,所以遇到 0xD800–0xDFFF 之內(nèi)的字符會(huì)因?yàn)闊o法編碼成 UTF-8 進(jìn)而導(dǎo)致顯示錯(cuò)誤。在 ES10 它會(huì)用轉(zhuǎn)義字符的方式來處理這部分字符而非編碼的方式,這樣就會(huì)正常顯示了。
//?\uD83D\uDE0E??emoji?多字節(jié)的一個(gè)字符
console.log(JSON.stringify('\uD83D\uDE0E'))?//?打印出笑臉
//?如果我們只去其中的一部分??\uD83D?這其實(shí)是個(gè)無效的字符串
//?之前的版本?,這些字符將替換為特殊字符,而現(xiàn)在將未配對(duì)的代理代碼點(diǎn)表示為JSON轉(zhuǎn)義序列
console.log(JSON.stringify('\uD83D'))?//?"\ud83d"
復(fù)制代碼
修訂 Function.prototype.toString()
以前函數(shù)的toString方法來自O(shè)bject.prototype.toString(),現(xiàn)在的 Function.prototype.toString() 方法返回一個(gè)表示當(dāng)前函數(shù)源代碼的字符串。以前只會(huì)返回這個(gè)函數(shù),不包含注釋、空格等。
function?foo()?{
????//?es10新特性
????console.log('imooc')
}
console.log(foo.toString())?
//?打印如下
//?function?foo()?{
//??//?es10新特性
//??console.log("imooc");
//?}
復(fù)制代碼
將返回注釋、空格和語法等詳細(xì)信息。
ES2020(ES11)
空值合并運(yùn)算符(Nullish coalescing Operator)
空值合并操作符( ?? )是一個(gè)邏輯操作符,當(dāng)左側(cè)的操作數(shù)為?null或者undefined時(shí),返回其右側(cè)操作數(shù),否則返回左側(cè)操作數(shù)。
const?foo?=?undefined????"foo"
const?bar?=?null????"bar"
console.log(foo)?//?foo
console.log(bar)?//?bar
復(fù)制代碼
與邏輯或操作符(||)不同,邏輯或操作符會(huì)在左側(cè)操作數(shù)為假值時(shí)返回右側(cè)操作數(shù)。也就是說,如果使用?||?來為某些變量設(shè)置默認(rèn)值,可能會(huì)遇到意料之外的行為。比如為假值(例如'',0,NaN,false)時(shí)。見下面的例子。
const?foo?=?""????'default?string';
const?foo2?=?""?||?'default?string';
console.log(foo);?//?""
console.log(foo2);?//?"default?string"
const?baz?=?0????42;
const?baz2?=?0?||?42;
console.log(baz);?//?0
console.log(baz2);?//?42
復(fù)制代碼
注意點(diǎn)
將????直接與?AND(&&)和 OR(||)操作符組合使用是不可取的。
null?||?undefined????"foo";?//?拋出?SyntaxError
true?||?undefined????"foo";?//?拋出?SyntaxError
復(fù)制代碼
可選鏈 Optional chaining
介紹
可選鏈操作符(??.?)允許讀取位于連接對(duì)象鏈深處的屬性的值,而不必明確驗(yàn)證鏈中的每個(gè)引用是否有效。?.?操作符的功能類似于?.?鏈?zhǔn)讲僮鞣?,不同之處在于,在引用?null?或者?undefined 的情況下不會(huì)引起錯(cuò)誤,該表達(dá)式短路返回值是?undefined。與函數(shù)調(diào)用一起使用時(shí),如果給定的函數(shù)不存在,則返回?undefined。
當(dāng)嘗試訪問可能不存在的對(duì)象屬性時(shí),可選鏈操作符將會(huì)使表達(dá)式更短、更簡(jiǎn)明。在探索一個(gè)對(duì)象的內(nèi)容時(shí),如果不能確定哪些屬性必定存在,可選鏈操作符也是很有幫助的。
const?user?=?{
????address:?{
????????street:?'xx街道',
????????getNum()?{
????????????return?'80號(hào)'
????????}
????}
}
復(fù)制代碼
在之前的語法中,想獲取到深層屬性或方法,不得不做前置校驗(yàn),否則很容易命中?Uncaught TypeError: Cannot read property...?這種錯(cuò)誤,這極有可能讓你整個(gè)應(yīng)用掛掉。
const?street?=?user?&&?user.address?&&?user.address.street
const?num?=?user?&&?user.address?&&?user.address.getNum?&&?user.address.getNum()
console.log(street,?num)
復(fù)制代碼
用了 Optional Chaining ,上面代碼會(huì)變成
const?street2?=?user?.address?.street
const?num2?=?user?.address?.getNum?.()
console.log(street2,?num2)
復(fù)制代碼
可選鏈中的 ? 表示如果問號(hào)左邊表達(dá)式有值, 就會(huì)繼續(xù)查詢問號(hào)后面的字段。根據(jù)上面可以看出,用可選鏈可以大量簡(jiǎn)化類似繁瑣的前置校驗(yàn)操作,而且更安全。
常見用法
??//?對(duì)象中使用
??let?obj?=?{
????name:?"jimmy",
????age:?"18",
??};
??let?property?=?"age";
??let?name?=?obj?.name;
??let?age?=?obj?.age;
??let?ages?=?obj?.[property];
??let?sex?=?obj?.sex;
??console.log(name);?//?jimmy
??console.log(age);?//?18
??console.log(ages);?//?18
??console.log(sex);?//?undefined
??
??//?數(shù)組中使用
??let?arr?=?[1,2,2];
??let?arrayItem?=?arr?.[42];?//?undefined
??
??//?函數(shù)中使用
??let?obj?=?{
???func:?function?()?{
?????console.log("I?am?func");
???},
??};
??obj?.func();?//?I?am?func
復(fù)制代碼
與空值合并操作符一起使用
let?customer?=?{
??name:?"jimmy",
??details:?{?age:?18?}
};
let?customerCity?=?customer?.city????"成都";
console.log(customerCity);?//?"成都"
復(fù)制代碼
注意點(diǎn)
可選鏈不能用于賦值
let?object?=?{};
object?.property?=?1;?//?Uncaught?SyntaxError:?Invalid?left-hand?side?in?assignment
復(fù)制代碼
globalThis
在以前,從不同的 JavaScript 環(huán)境中獲取全局對(duì)象需要不同的語句。在 Web 中,可以通過?window、self?取到全局對(duì)象,在 Node.js 中,它們都無法獲取,必須使用?global。
在松散模式下,可以在函數(shù)中返回?this?來獲取全局對(duì)象,但是在嚴(yán)格模式和模塊環(huán)境下,this?會(huì)返回?undefined。
以前想要獲取全局對(duì)象,可通過一個(gè)全局函數(shù)
const?getGlobal?=?()?=>?{
????if?(typeof?self?!==?'undefined')?{
????????return?self
????}
????if?(typeof?window?!==?'undefined')?{
????????return?window
????}
????if?(typeof?global?!==?'undefined')?{
????????return?global
????}
????throw?new?Error('無法找到全局對(duì)象')
}
const?globals?=?getGlobal()
console.log(globals)
復(fù)制代碼
現(xiàn)在globalThis?提供了一個(gè)標(biāo)準(zhǔn)的方式來獲取不同環(huán)境下的全局?this? 對(duì)象(也就是全局對(duì)象自身)。不像?window?或者?self?這些屬性,它確??梢栽谟袩o窗口的各種環(huán)境下正常工作。所以,你可以安心的使用?globalThis,不必?fù)?dān)心它的運(yùn)行環(huán)境。
為便于記憶,你只需要記住,全局作用域中的?this?就是globalThis。以后就用globalThis就行了。
BigInt
BigInt?是一種內(nèi)置對(duì)象,它提供了一種方法來表示大于?2的53次方 \- 1?的整數(shù)。這原本是 Javascript中可以用?Number?表示的最大數(shù)字。BigInt?可以表示任意大的整數(shù)。
使用 BigInt 有兩種方式:
方式一:數(shù)字后面增加n
const?bigInt?=?9007199254740993n
console.log(bigInt)
console.log(typeof?bigInt)?//?bigint
//?`BigInt`?和?[`Number`]不是嚴(yán)格相等的,但是寬松相等的。
console.log(1n?==?1)?//?true
console.log(1n?===?1)?//?false
//?`Number`?和?`BigInt`?可以進(jìn)行比較。
1n?2?//???true
2n?>?1?//???true
復(fù)制代碼
方式二:使用 BigInt 函數(shù)
const?bigIntNum?=?BigInt(9007199254740993n)
console.log(bigIntNum)
復(fù)制代碼
運(yùn)算
let?number?=?BigInt(2);
let?a?=?number?+?2n;?//?4n
let?b?=?number?*?10n;?//?20n
let?c?=?number?-?10n;?//?-8n
console.log(a);
console.log(b);
console.log(c);
復(fù)制代碼
注意點(diǎn)
BigInt不能用于?[Math]?對(duì)象中的方法;不能和任何?[Number]?實(shí)例混合運(yùn)算,兩者必須轉(zhuǎn)換成同一種類型。在兩種類型來回轉(zhuǎn)換時(shí)要小心,因?yàn)?BigInt?變量在轉(zhuǎn)換成?[Number]?變量時(shí)可能會(huì)丟失精度。
String.prototype.matchAll()
matchAll() ?方法返回一個(gè)包含所有匹配正則表達(dá)式的結(jié)果及分組捕獲組的迭代器。
const?regexp?=?/t(e)(st(\d?))/g;
const?str?=?'test1test2';
const?array?=?[...str.matchAll(regexp)];
console.log(array[0]);??//?["test1",?"e",?"st1",?"1"]
console.log(array[1]);?//?["test2",?"e",?"st2",?"2"]
復(fù)制代碼
Promise.allSettled()
我們都知道 Promise.all() 具有并發(fā)執(zhí)行異步任務(wù)的能力。但它的最大問題就是如果其中某個(gè)任務(wù)出現(xiàn)異常(reject),所有任務(wù)都會(huì)掛掉,Promise直接進(jìn)入reject 狀態(tài)。
場(chǎng)景:現(xiàn)在頁面上有三個(gè)請(qǐng)求,分別請(qǐng)求不同的數(shù)據(jù),如果一個(gè)接口服務(wù)異常,整個(gè)都是失敗的,都無法渲染出數(shù)據(jù)
我們需要一種機(jī)制,如果并發(fā)任務(wù)中,無論一個(gè)任務(wù)正?;蛘弋惓?,都會(huì)返回對(duì)應(yīng)的的狀態(tài),這就是Promise.allSettled的作用
const?promise1?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????resolve("promise1");
??????//???reject("error?promise1?");
????},?3000);
??});
};
const?promise2?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????resolve("promise2");
??????//???reject("error?promise2?");
????},?1000);
??});
};
const?promise3?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????//???resolve("promise3");
??????reject("error?promise3?");
????},?2000);
??});
};
//??Promise.all?會(huì)走到catch里面
Promise.all([promise1(),?promise2(),?promise3()])
??.then((res)?=>?{
????console.log(res);?
??})
??.catch((error)?=>?{
????console.log("error",?error);?//?error?promise3?
??});
??
//?Promise.allSettled?不管有沒有錯(cuò)誤,三個(gè)的狀態(tài)都會(huì)返回
Promise.allSettled([promise1(),?promise2(),?promise3()])
??.then((res)?=>?{
????console.log(res);??
????//?打印結(jié)果?
????//?[
????//????{status:?'fulfilled',?value:?'promise1'},?
????//????{status:?'fulfilled',value:?'promise2'},
????//????{status:?'rejected',?reason:?'error?promise3?'}
????//?]
??})
??.catch((error)?=>?{
????console.log("error",?error);?
??});
復(fù)制代碼
Dynamic Import(按需 import)
import()可以在需要的時(shí)候,再加載某個(gè)模塊。
button.addEventListener('click',?event?=>?{
??import('./dialogBox.js')
??.then(dialogBox?=>?{
????dialogBox.open();
??})
??.catch(error?=>?{
????/*?Error?handling?*/
??})
});
復(fù)制代碼
上面代碼中,import()方法放在click事件的監(jiān)聽函數(shù)之中,只有用戶點(diǎn)擊了按鈕,才會(huì)加載這個(gè)模塊。
ES2021(ES12)
邏輯運(yùn)算符和賦值表達(dá)式(&&=,||=,??=)
&&=
邏輯與賦值 x &&= y等效于:
x?&&?(x?=?y);
復(fù)制代碼
上面的意思是,當(dāng)x為真時(shí),x=y。具體請(qǐng)看下面的示例:
let?a?=?1;
let?b?=?0;
a?&&=?2;
console.log(a);?//?2
b?&&=?2;
console.log(b);??//?0
復(fù)制代碼
||=
邏輯或賦值(x ||= y)運(yùn)算僅在?x?為false時(shí)賦值。
x ||= y?等同于:x || (x = y);
const?a?=?{?duration:?50,?title:?''?};
a.duration?||=?10;
console.log(a.duration);?//?50
a.title?||=?'title?is?empty.';
console.log(a.title);?//?"title?is?empty"
復(fù)制代碼
??=
邏輯空賦值運(yùn)算符?(x ??= y) 僅在?x?是?nullish[3]?(null?或?undefined) 時(shí)對(duì)其賦值。
x ??= y?等價(jià)于:x ?? (x = y);
示例一
const?a?=?{?duration:?50?};
a.duration???=?10;
console.log(a.duration);?//?50
a.speed???=?25;
console.log(a.speed);?//?25
復(fù)制代碼
示例二
function?config(options)?{
??options.duration???=?100;
??options.speed???=?25;
??return?options;
}
config({?duration:?125?});?//?{?duration:?125,?speed:?25?}
config({});?//?{?duration:?100,?speed:?25?}
復(fù)制代碼
String.prototype.replaceAll()
介紹
replaceAll() ?方法返回一個(gè)新字符串,新字符串中所有滿足?pattern?的部分都會(huì)被replacement?替換。pattern可以是一個(gè)字符串或一個(gè)RegExp,replacement可以是一個(gè)字符串或一個(gè)在每次匹配被調(diào)用的函數(shù)。
原始字符串保持不變。
示例
'aabbcc'.replaceAll('b',?'.');?//?'aa..cc'
復(fù)制代碼
使用正則表達(dá)式搜索值時(shí),它必須是全局的。
'aabbcc'.replaceAll(/b/,?'.');
TypeError:?replaceAll?must?be?called?with?a?global?RegExp
復(fù)制代碼
這將可以正常運(yùn)行:
'aabbcc'.replaceAll(/b/g,?'.');
"aa..cc"
復(fù)制代碼
數(shù)字分隔符
歐美語言中,較長(zhǎng)的數(shù)值允許每三位添加一個(gè)分隔符(通常是一個(gè)逗號(hào)),增加數(shù)值的可讀性。比如,1000可以寫作1,000。
ES2021中允許 JavaScript 的數(shù)值使用下劃線(_)作為分隔符。
let?budget?=?1_000_000_000_000;
budget?===?10?**?12?//?true
復(fù)制代碼
這個(gè)數(shù)值分隔符沒有指定間隔的位數(shù),也就是說,可以每三位添加一個(gè)分隔符,也可以每一位、每?jī)晌弧⒚克奈惶砑右粋€(gè)。
123_00?===?12_300?//?true
12345_00?===?123_4500?//?true
12345_00?===?1_234_500?//?true
復(fù)制代碼
小數(shù)和科學(xué)計(jì)數(shù)法也可以使用數(shù)值分隔符。
//?小數(shù)
0.000_001
//?科學(xué)計(jì)數(shù)法
1e10_000
復(fù)制代碼
數(shù)值分隔符有幾個(gè)使用注意點(diǎn)。
不能放在數(shù)值的最前面(leading)或最后面(trailing)。 不能兩個(gè)或兩個(gè)以上的分隔符連在一起。 小數(shù)點(diǎn)的前后不能有分隔符。 科學(xué)計(jì)數(shù)法里面,表示指數(shù)的 e或E前后不能有分隔符。
下面的寫法都會(huì)報(bào)錯(cuò)。
//?全部報(bào)錯(cuò)
3_.141
3._141
1_e12
1e_12
123__456
_1464301
1464301_
復(fù)制代碼
Promise.any
方法接受一組 Promise 實(shí)例作為參數(shù),包裝成一個(gè)新的 Promise 實(shí)例返回。
const?promise1?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????resolve("promise1");
??????//??reject("error?promise1?");
????},?3000);
??});
};
const?promise2?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????resolve("promise2");
??????//?reject("error?promise2?");
????},?1000);
??});
};
const?promise3?=?()?=>?{
??return?new?Promise((resolve,?reject)?=>?{
????setTimeout(()?=>?{
??????resolve("promise3");
??????//?reject("error?promise3?");
????},?2000);
??});
};
Promise.any([promise1(),?promise2(),?promise3()])
??.then((first)?=>?{
????//?只要有一個(gè)請(qǐng)求成功?就會(huì)返回第一個(gè)請(qǐng)求成功的
????console.log(first);?//?會(huì)返回promise2
??})
??.catch((error)?=>?{
????//?所有三個(gè)全部請(qǐng)求失敗?才會(huì)來到這里
????console.log("error",?error);
??});
復(fù)制代碼
只要參數(shù)實(shí)例有一個(gè)變成fulfilled狀態(tài),包裝實(shí)例就會(huì)變成fulfilled狀態(tài);如果所有參數(shù)實(shí)例都變成rejected狀態(tài),包裝實(shí)例就會(huì)變成rejected狀態(tài)。
Promise.any()跟Promise.race()方法很像,只有一點(diǎn)不同,就是Promise.any()不會(huì)因?yàn)槟硞€(gè) Promise 變成rejected狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise 變成rejected狀態(tài)才會(huì)結(jié)束。
WeakRef and Finalizers
這兩個(gè)新特性,都應(yīng)該盡量避免使用,所以這里不做過多的講解。如感興趣,請(qǐng)參考
WeakRef[4]
Finalizers[5]
關(guān)于本文
作者:Jimmy_fx
https://juejin.cn/post/7046217976176967711
