日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
「建議收藏」送你一份精心總結(jié)的3萬字ES6實用指南(下)

 由于篇幅限制,所以將文章分成了 2 篇,這是下篇,包含了從 ES2016 到 ES2021 的所有知識。

創(chuàng)新互聯(lián)建站主要從事網(wǎng)站設(shè)計制作、網(wǎng)站制作、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)懷遠,十多年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

「建議收藏」送你一份精心總結(jié)的3萬字ES6實用指南(上)

「建議收藏」送你一份精心總結(jié)的3萬字ES6實用指南(下)

 

ES2016

Array.prototype.includes

判斷一個數(shù)組是否包含某個元素,之前一般是這么做的:

 
 
 
 
  1. if (arr.indexOf(el) >= 0) {} 
  2.  
  3. // 或者 
  4. if (~arr.indexOf(el)) {} 

而現(xiàn)在你可以這么做了:

 
 
 
 
  1. if (arr.includes(el)) {} 

indexOf 會返回找到元素在數(shù)組中的索引位置,判斷的邏輯是是否嚴格相等,所以他在遇到 NaN 的時候不能正確返回索引,但是 includes 解決了這個問題:

 
 
 
 
  1. [1, NaN, 3].indexOf(NaN)   // -1 
  2. [1, NaN, 3].includes(NaN)  // true 

求冪運算符(**)

x ** y 是求 x 的 y 次冪,和 Math.pow(x, y) 功能一致:

 
 
 
 
  1. // x ** y 
  2. let squared = 2 ** 2  // 2 * 2 = 4 
  3. let cubed = 2 ** 3    // 2 * 2 * 2 = 8 

x **= y 表示求 x 的 y 次冪,并且把結(jié)果賦值給 x:

 
 
 
 
  1. // x **= y 
  2. let x = 2; 
  3. x **= 3  // x 最后等于 8 

ES2017

Object.values()

返回一個由對象自身所有可遍歷屬性的屬性值組成的數(shù)組:

 
 
 
 
  1. const person = { name: '布蘭' }; 
  2. Object.defineProperty(person, 'age', { 
  3.     value: 12, 
  4.     enumrable: false  // age 屬性將不可遍歷 
  5. }) 
  6. console.log(Object.values(person))  // ['布蘭'] 
  7.  
  8. // 類似 str.split('') 效果 
  9. console.log(Object.values('abc'))  // ['a', 'b', 'c'] 

Object.entries()

返回一個由對象自身所有可遍歷屬性的鍵值對組成的數(shù)組:

 
 
 
 
  1. const person = { name: '布蘭', age: 12 } 
  2. console.log(Object.entries(person))  // [["name", "布蘭"], ["age", 12]] 

利用這個方法可以很好的將對象轉(zhuǎn)成正在的 Map 結(jié)構(gòu):

 
 
 
 
  1. const person = { name: '布蘭', age: 12 } 
  2. const map = new Map(Object.entries(person)) 
  3. console.log(map)  // Map { name: '布蘭', age: 12 } 

Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptor() 會返回指定對象某個自身屬性的的描述對象,而 Object.getOwnPropertyDescriptors() 則是返回指定對象自身所有屬性的描述對象:

 
 
 
 
  1. const person = { name: '布蘭', age: 12 } 
  2.  
  3. console.log(Object.getOwnPropertyDescriptor(person, 'name')) 
  4. // { configurable: true, enumerable: true, value: "布蘭", writable: true } 
  5.  
  6. console.log(Object.getOwnPropertyDescriptors(person)) 
  7. //{  
  8. //  name: { configurable: true, enumerable: true, value: "布蘭", writable: true }, 
  9. //  age: {configurable: false, enumerable: false, value: 12, writable: false} 
  10. //} 

配合 Object.create() 可以實現(xiàn)淺克?。?/p>

 
 
 
 
  1. const shallowClone = (obj) => Object.create( 
  2.     Object.getPrototypeOf(obj), 
  3.     Object.getOwnPropertyDescriptors(obj) 

String.prototype.padStart()

str.padStart(length [, padStr]) 會返回一個新字符串,該字符串將從 str 字符串的左側(cè)開始填充某個字符串 padStr(非必填,如果不是字符串則會轉(zhuǎn)成字符串, 傳入 undefined 和不傳這個參數(shù)效果一致)直到達到指定位數(shù) length 為止:

 
 
 
 
  1. 'abc'.padStart(5, 2)          // '22abc' 
  2. 'abc'.padStart(5, undefined)  // '  abc' 
  3. 'abc'.padStart(5, {})         // '[oabc' 
  4. 'abc'.padStart(5)             // '  abc' 
  5. 'abcde'.padStart(2, 'f')      // 'abcde' 

String.prototype.padEnd()

規(guī)則和 padStart 類似,但是是從字符串右側(cè)開始填充:

 
 
 
 
  1. 'abc'.padEnd(5, 2)  // 'abc22' 

函數(shù)參數(shù)尾逗號

允許函數(shù)在定義和調(diào)用的時候時候最后一個參數(shù)后加上逗號:

 
 
 
 
  1. function init( 
  2.     param1,  
  3.     param2,   
  4. ) { } 
  5.  
  6. init('a', 'b',) 

Async函數(shù)

使用 async 可以聲明一個 async 函數(shù),結(jié)合 await 可以用一種很簡介的方法寫成基于 Promise 的異步行為,而不需要刻意的鏈式調(diào)用。await 表達式會暫停整個 async 函數(shù)的執(zhí)行進程并出讓其控制權(quán),只有當(dāng)其等待的基于 Promise 的異步操作被兌現(xiàn)或被拒絕之后才會恢復(fù)進程。async 函數(shù)有如下幾種定義形式:

 
 
 
 
  1. // 函數(shù)聲明 
  2. async function foo() {} 
  3.  
  4. // 函數(shù)表達式 
  5. let foo = async function() {} 
  6.  
  7. // 箭頭函數(shù) 
  8. let foo = async () => {} 
  9.  
  10. // 對象方法 
  11. lef obj = { 
  12.     async foo() {} 
  13.  
  14. // 類方法 
  15. class Dog { 
  16.     async bark() {} 

async 函數(shù)一定會返回一個 Promise 對象,所以它可以使用 then 添加處理函數(shù)。如果一個 async 函數(shù)的返回值看起來不是Promise,那么它將會被隱式地包裝在一個 Promise 中:

 
 
 
 
  1. async function foo() { 
  2.     return 'a' 
  3. foo().then(res => { 
  4.     console.log(res)  // 'a' 
  5. }) 

內(nèi)部如果發(fā)生錯誤,或者顯示拋出錯誤,那么 async 函數(shù)會返回一個 rejected 狀態(tài)的 Promsie:

 
 
 
 
  1. async function foo() { 
  2.     throw new Error('error') 
  3. foo().catch(err => { 
  4.     console.log(err)  // Error: error 
  5. }) 

返回的 Promise 對象必須等到內(nèi)部所有 await 命令 Promise 對象執(zhí)行完才會發(fā)生狀態(tài)改變,除非遇到 return 語句或拋出錯誤;任何一個 await 命令返回的 Promise 對象變 為rejected 狀態(tài),整個 Async 函數(shù)都會中斷后續(xù)執(zhí)行:

 
 
 
 
  1. async function fn() { 
  2.     let a = await Promise.resolve('success') 
  3.     console.log('a_' + a) 
  4.     let b = await Promise.reject('fail') 
  5.     console.log('b_' + b)  // 不會執(zhí)行 
  6. fn().then(res => { 
  7.     console.log(res)  // 不會執(zhí)行 
  8. }, err => { 
  9.     console.log(err) 
  10. }) 
  11. // 'a_success' 
  12. // 'fail' 

所以為了保證 async 里的異步操作都能完成,我們需要將他們放到 try...catch() 塊里或者在 await 返回的 Promise 后跟一個 catch 處理函數(shù):

 
 
 
 
  1. async function fn() { 
  2.     try { 
  3.         let a = await Promise.reject('a fail') 
  4.         console.log('a_' + a)  // 不會執(zhí)行 
  5.     } catch (e) { 
  6.         console.log(e)  // 'a fail' 
  7.     } 
  8.     let b = await Promise.reject('b fail') 
  9.         .catch(e => { 
  10.             console.log(e)  // 'b fail' 
  11.         }) 
  12.     console.log('b_' + b)  // 'bundefined' 
  13. fn().then(res => { 
  14.     console.log(res)  // undefined 
  15. }, err => { 
  16.     console.log(err)  // 不會執(zhí)行 
  17. }) 

如果 async 函數(shù)里的多個異步操作之間沒有依賴關(guān)系,建議將他們寫到一起減少執(zhí)行時間:

 
 
 
 
  1. // 寫法一 
  2. let [foo, bar] = await Promise.all([getFoo(), getBar()]) 
  3.  
  4. // 寫法二 
  5. let fooPromise = getFoo() 
  6. let barPromise = getBar() 
  7. let foo = await fooPromise 
  8. let bar = await barPromise 

await 命令只能用在 async 函數(shù)之中,如果用在普通函數(shù),就會報錯。

共享內(nèi)存和Atomics對象

  • SharedArrayBuffer
  • Atomics

ES2018

Promise.prototype.finally()

Promise.prototype.finally() 用于給 Promise 對象添加 onFinally 函數(shù),這個函數(shù)主要是做一些清理的工作,只有狀態(tài)變化的時候才會執(zhí)行該 onFinally 函數(shù)。

 
 
 
 
  1. function onFinally() { 
  2.     console.log(888)  // 并不會執(zhí)行   
  3. new Promise((resolve, reject) => { 
  4.      
  5. }).finally(onFinally) 

finally() 會生成一個 Promise 新實例,finally 一般會原樣后傳父 Promise,無論父級實例是什么狀態(tài):

 
 
 
 
  1. let p1 = new Promise(() => {}) 
  2. let p2 = p1.finally(() => {}) 
  3. setTimeout(console.log, 0, p2)  // Promise {
  4.  
  5. let p3 = new Promise((resolve, reject) => { 
  6.     resolve(3) 
  7. }) 
  8. let p4 = p3.finally(() => {}) 
  9. setTimeout(console.log, 0, p3)  // Promise {: 3} 

上面說的是一般,但是也有特殊情況,比如 finally 里返回了一個非 fulfilled 的 Promise 或者拋出了異常的時候,則會返回對應(yīng)狀態(tài)的新實例:

 
 
 
 
  1. let p1 = new Promise((resolve, reject) => { 
  2.     resolve(3) 
  3. }) 
  4. let p2 = p1.finally(() => new Promise(() => {})) 
  5. setTimeout(console.log, 0, p2)  // Promise {
  6.  
  7. let p3 = p1.finally(() => Promise.reject(6)) 
  8. setTimeout(console.log, 0, p3)  // Promise {: 6} 
  9.  
  10. let p4 = p1.finally(() => { 
  11.     throw new Error('error') 
  12. }) 
  13. setTimeout(console.log, 0, p4)  // Promise {: Error: error} 

參考:

  • 深入理解Promise

異步迭代器

想要了解異步迭代器最好的方式就是和同步迭代器進行對比。我們知道可迭代數(shù)據(jù)的內(nèi)部都是有一個 Symbol.iterator 屬性,它是一個函數(shù),執(zhí)行后會返回一個迭代器對象,這個迭代器對象有一個 next() 方法可以對數(shù)據(jù)進行迭代,next() 執(zhí)行后會返回一個對象,包含了當(dāng)前迭代值 value 和 標識是否完成迭代的 done 屬性:

 
 
 
 
  1. let iterator = [1, 2][Symbol.iterator]() 
  2. iterator.next()  // { value: 1, done: false } 
  3. iterator.next()  // { value: 2, done: false } 
  4. iterator.next()  // { value: undefinde, done: true } 

上面這里的 next() 執(zhí)行的是同步操作,所以這個是同步迭代器,但是如果 next() 里需要執(zhí)行異步操作,那就需要異步迭代了,可異步迭代數(shù)據(jù)的內(nèi)部有一個 Symbol.asyncIterator屬性,基于此我們來實現(xiàn)一個異步迭代器:

 
 
 
 
  1. class Emitter { 
  2.     constructor(iterable) { 
  3.         this.data = iterable 
  4.     } 
  5.     [Symbol.asyncIterator]() { 
  6.         let length = this.data.length, 
  7.             index = 0; 
  8.          
  9.         return { 
  10.             next:() => { 
  11.                 const done = index >= length 
  12.                 const value = !done ? this.data[index++] : undefined 
  13.                 return new Promise((resolve, reject) => { 
  14.                     resolve({value, done}) 
  15.                 }) 
  16.             } 
  17.         } 
  18.     } 

異步迭代器的 next() 會進行異步的操作,通常是返回一個 Promise,所以需要對應(yīng)的處理函數(shù)去處理結(jié)果:

 
 
 
 
  1. let emitter = new Emitter([1, 2, 3]) 
  2. let asyncIterator = emitter[Symbol.asyncIterator]() 
  3. asyncIterator.next().then(res => { 
  4.     console.log(res)  // { value: 1, done: false } 
  5. }) 
  6. asyncIterator.next().then(res => { 
  7.     console.log(res)  // { value: 2, done: false } 
  8. }) 
  9. asyncIterator.next().then(res => { 
  10.     console.log(res)  // { value: 3, done: false } 
  11. }) 

另外也可以使用 for await...of 來迭代異步可迭代數(shù)據(jù):

 
 
 
 
  1. let asyncIterable = new Emitter([1, 2, 3]) 
  2. async function asyncCount() { 
  3.     for await (const x of asyncIterable ) { 
  4.         console.log(x) 
  5.     } 
  6. asyncCount() 
  7. // 1 2 3 

另外還可以通過異步生成器來創(chuàng)建異步迭代器:

 
 
 
 
  1. class Emitter { 
  2.     constructor(iterable) { 
  3.         this.data = iterable 
  4.     } 
  5.     async *[Symbol.asyncIterator]() { 
  6.         let length = this.data.length, 
  7.             index = 0; 
  8.              
  9.         while (index < length) { 
  10.             yield this.data[index++] 
  11.         } 
  12.     } 
  13. async function asyncCount() { 
  14.     let emitter = new Emitter([1, 2, 3]) 
  15.     const asyncIterable = emitter[Symbol.asyncIterator]() 
  16.     for await (const x of asyncIterable ) { 
  17.         console.log(x) 
  18.     } 
  19. asyncCount() 
  20. // 1 2 3 

參考:

  • Iteration_protocols
  • for-await...of

s修飾符(dotAll模式)

正則表達式新增了一個 s 修飾符,使得 . 可以匹配任意單個字符:

 
 
 
 
  1. /foo.bar/.test('foo\nbar')   // false 
  2. /foo.bar/s.test('foo\nbar')  // true 

上面這又被稱為 dotAll 模式,表示點(dot)代表一切字符。所以,正則表達式還引入了一個dotAll屬性,返回一個布爾值,表示該正則表達式是否處在dotAll模式:

 
 
 
 
  1. /foo.bar/s.dotAll  // true 

具名組匹配

正則表達式可以使用捕獲組來匹配字符串,但是想要獲取某個組的結(jié)果只能通過對應(yīng)的索引來獲?。?/p>

 
 
 
 
  1. let re = /(\d{4})-(\d{2})-(\d{2})/ 
  2. let result = re.exec('2015-01-02') 
  3. // result[0] === '2015-01-02' 
  4. // result[1] === '2015' 
  5. // result[2] === '01' 
  6. // result[3] === '02' 

而現(xiàn)在我們可以通過給捕獲組 (? ...) 加上名字 name ,通過名字來獲取對應(yīng)組的結(jié)果:

 
 
 
 
  1. let re = /(?\d{4})-(?\d{2})-(?\d{2})/ 
  2. let result = re.exec('2015-01-02') 
  3. // result.groups.year === '2015' 
  4. // result.groups.month === '01' 
  5. // result.groups.day === '02' 

配合解構(gòu)賦值可以寫出非常精簡的代碼:

 
 
 
 
  1. let {groups: {year, month, day}} = /(?\d{4})-(?\d{2})-(?\d{2})/.exec('2015-01-02') 
  2. console.log(year, month, day)  // 2015 01 02 

具名組也可以通過傳遞給 String.prototype.replace 的替換值中進行引用。如果該值為字符串,則可以使用 $ 獲取到對應(yīng)組的結(jié)果:

 
 
 
 
  1. let re = /(?\d{4})-(?\d{2})-(?\d{2})/ 
  2. let result = '2015-01-02'.replace(re, '$/$/$') 
  3. // result === '02/01/2015' 

參考:

  • proposal-regexp-named-groups

后行斷言

后行斷言: (?<=y)x,x 只有在 y 后面才能匹配:

 
 
 
 
  1. /(?<=\$)\d+/.exec('I have $100.')  // ['100'] 

后行否定斷言: (?<=y)x,x 只有在 y 后面才能匹配:

 
 
 
 
  1. /(?

Unicode屬性轉(zhuǎn)義

允許正則表達式匹配符合 Unicode 某種屬性的所有字符,\p{...} 是匹配包含,\P{...}是匹配不包含的字符,且必須搭配 /u 修飾符才會生效:

 
 
 
 
  1. /\p{Emoji}+/u.exec('笑死我了????不行了')  // [''] 
  2. /\P{Emoji}+/u.exec('笑死我了????不行了')  // ['笑死我了'] 

這里可以查詢到更多的 Unicode 的屬性 Full_Properties

對象擴展運算符

對象的擴展運算符可以用到解構(gòu)賦值上,且只能應(yīng)用到最后一個變量上:

 
 
 
 
  1. let {x, ...y} = {x: 1, a: 2, b: 3} 
  2. console.log(y)  // {a: 2, b: 3} 

對象擴展運算符不能解構(gòu)原型上的屬性:

 
 
 
 
  1. let obj = { x: 1 } 
  2. obj.__proto__ = { y: 2 } 
  3. let {...a} = obj 
  4. console.log(a.y)  // undefined 

應(yīng)用一:可以實現(xiàn)淺拷貝,但是不會拷貝原始屬性:

 
 
 
 
  1. let person = Object.create({ name: '布蘭' }) 
  2. person.age = 12 
  3.  
  4. // 淺拷貝寫法一 
  5. let { ...pClone1 } = person 
  6. console.log(pClone1)  // { age: 12 } 
  7. console.log(pClone1.name)  // undefined 
  8.  
  9. // 淺拷貝寫法二 
  10. let pClone2 = {...person} 
  11. console.log(pClone2)  // { age: 12 } 
  12. console.log(pClone2.name)  // undefined 

應(yīng)用二:合并兩個對象:

 
 
 
 
  1. let ab = {...a, ...b} 
  2.  
  3. // 等同于 
  4. let ab = Object.assign({}, a, b); 

應(yīng)用三:重寫對象屬性

 
 
 
 
  1. let aWithOverrides = { ...a, x: 1, y: 2 }; 

應(yīng)用四:給新對象設(shè)置默認值

 
 
 
 
  1. let aWithDefaults = { x: 1, y: 2, ...a }; 

應(yīng)用五:利用擴展運算符的解構(gòu)賦值可以擴展函數(shù)參數(shù):

 
 
 
 
  1. function baseFunction({ a, b }) {} 
  2. function wrapperFunction({ x, y, ...restConfig }) { 
  3.     // 使用 x 和 y 參數(shù)進行操作 
  4.     // 其余參數(shù)傳給原始函數(shù) 
  5.     return baseFunction(restConfig) 

參考:

  • Object Spread Initializer
  • Object Rest Destructuring

放松對標簽?zāi)0謇镒址D(zhuǎn)義的限制

參考:

  • ECMAScript 6 入門

ES2019

允許省略catch里的參數(shù)

異常被捕獲的時候如果不需要做操作,甚至可以省略 catch(err) 里的參數(shù)和圓括號:

 
 
 
 
  1. try { 
  2.  
  3. } catch { 
  4.      

JSON.stringify()變動

UTF-8 標準規(guī)定,0xD800 到 0xDFFF 之間的碼點,不能單獨使用,必須配對使用。 所以 JSON.stringify() 對單個碼點進行操作,如果碼點符合 UTF-8 標準,則會返回對應(yīng)的字符,否則會返回對應(yīng)的碼點:

 
 
 
 
  1. JSON.stringify('\u{1f600}')  // """" 
  2. JSON.stringify('\u{D834}')  // ""\ud834"" 

Symbol.prototype.description

Symbol 實例新增了一個描述屬性 description:

 
 
 
 
  1. let symbol = Symbol('foo') 
  2. symbol.description  // 'foo' 

Function.prototype.toString()

函數(shù)的 toString() 會原樣輸出函數(shù)定義時候的樣子,不會省略注釋和空格。

Object.fromEntries()

Object.fromEntries() 方法是 Object.entries() 的逆操作,用于將一個鍵值對數(shù)組轉(zhuǎn)為對象:

 
 
 
 
  1. let person = { name: '布蘭', age: 12 } 
  2. let keyValueArr = Object.entries(person)   // [['name', '布蘭'], ['age', 12]] 
  3. let obj = Object.fromEntries(arr)  // { name: '布蘭', age: 12 } 

常用可迭代數(shù)據(jù)結(jié)構(gòu)之間的裝換:

 
 
 
 
  1. let person = { name: '布蘭', age: 12 } 
  2.  
  3. // 對象 -> 鍵值對數(shù)組 
  4. let keyValueArr = Object.entries(person)  // [['name', '布蘭'], ['age', 12]] 
  5.  
  6. // 鍵值對數(shù)組 -> Map 
  7. let map = new Map(keyValueArr)  // Map {"name": "布蘭", "age": 12} 
  8.  
  9. // Map -> 鍵值對數(shù)組 
  10. let arr = Array.from(map)  // [['name', '布蘭'], ['age', 12]]  
  11.  
  12. // 鍵值對數(shù)組 -> 對象 
  13. let obj = Array.from(arr).reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {})  // { name: '布蘭', age: 12 } 

參考:

  • Object.fromEntries

字符串可直接輸入行分隔符和段分隔符

JavaScript 規(guī)定有 5 個字符,不能在字符串里面直接使用,只能使用轉(zhuǎn)義形式。

  • U+005C:反斜杠(reverse solidus)
  • U+000D:回車(carriage return)
  • U+2028:行分隔符(line separator)
  • U+2029:段分隔符(paragraph separator)
  • U+000A:換行符(line feed)

但是由于 JSON 允許字符串里可以使用 U+2028 和 U+2029,所以使得 JSON.parse() 去解析字符串的時候可能會報錯,所以 ES2019 允許模板字符串里可以直接這兩個字符:

 
 
 
 
  1. JSON.parse('"\u2028"')  // "" 
  2. JSON.parse('"\u2029"')  // "" 
  3. JSON.parse('"\u005C"')  // SyntaxError 

String.prototype.trimStart

消除字符串頭部空格,返回一個新字符串;瀏覽器還額外增加了它的別名函數(shù) trimLeft():

 
 
 
 
  1. let str = '  hello world ' 
  2. let newStr = str.trimStart() 
  3. console.log(newStr, newStr === str)  
  4. // 'hello world '  false 

String.prototype.trimEnd

消除字符串尾部空格,返回一個新字符串;瀏覽器還額外增加了它的別名函數(shù) trimRight():

 
 
 
 
  1. let str = '  hello world ' 
  2. let newStr = str.trimEnd() 
  3. console.log(newStr, newStr === str)  
  4. // '  hello world'  false 

Array.prototype.flat()

arr.flat(depth) 按照 depth (不傳值的話默認是 1)深度拍平一個數(shù)組,并且將結(jié)果以新數(shù)組形式返回:

 
 
 
 
  1. // depth 默認是 1 
  2. const arr1 = [1, 2, [3, 4]] 
  3. console.log(arr1.flat())  // [1, 2, 3, 4] 
  4.  
  5. // 使用 Infinity,可展開任意深度的嵌套數(shù)組;自動跳過空數(shù)組; 
  6. const arr2 = [1, , [2, [3, [4]]]] 
  7. console.log(arr2.flat(Infinity)) 
  8. // [1, 2, 3, 4] 

用 reduce 實現(xiàn)拍平一層數(shù)組:

 
 
 
 
  1. const arr = [1, 2, [3, 4]] 
  2.  
  3. // 方法一 
  4. let newStr = arr.reduce((acc, cur) => acc.concat(cur), []) 
  5.  
  6. // 方法二 
  7. const flattened = arr => [].concat(...arr) 
  8. flattened(arr) 

參考:

  • flat

Array.prototype.flatMap()

flatMap(callback) 使用映射函數(shù) callback 映射每個元素,callback 每次的返回值組成一個數(shù)組,并且將這個數(shù)組執(zhí)行類似 arr.flat(1) 的操作進行拍平一層后最后返回結(jié)果:

 
 
 
 
  1. const arr1 = [1, 2, 3, 4] 
  2.  
  3. arr1.flatMap(x => [x * 2]) 
  4. // 將 [[2], [4], [6], [8]] 數(shù)組拍平一層得到最終結(jié)果:[2, 4, 6, 8] 

參考:

  • flatMap

ES2020

String.prototype.matchAll()

String.prototype.matchAll() 方法,可以一次性取出所有匹配。不過,它返回的是一個 RegExpStringIterator 迭代器同是也是一個可迭代的數(shù)據(jù)結(jié)構(gòu),所以可以通過 for...of 進行迭代:

 
 
 
 
  1. let str = 'test1test2' 
  2. let regexp = /t(e)(st(\d?))/g 
  3. let iterable = str.matchAll(regexp) 
  4. for (const x of iterable) { 
  5.     console.log(x) 
  6. // ['test1', 'e', 'st1', '1', index: 0, input: 'test1test1', groups: undefined] 
  7. // ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', groups: undefined] 

注意當(dāng)使用 matchAll(regexp) 的時候,正則表達式必須加上 /g 修飾符。

也可以將這個可迭代數(shù)據(jù)轉(zhuǎn)成數(shù)組形式:

 
 
 
 
  1. // 方法一 
  2. [...str.matchAll(regexp)] 
  3.  
  4. // 方法二 
  5. Array.from(str.matchAll(regexp)) 

動態(tài)import()

標準用法的 import 導(dǎo)入的模塊是靜態(tài)的,會使所有被導(dǎo)入的模塊,在加載時就被編譯(無法做到按需編譯,降低首頁加載速度)。有些場景中,你可能希望根據(jù)條件導(dǎo)入模塊或者按需導(dǎo)入模塊,這時你可以使用動態(tài)導(dǎo)入代替靜態(tài)導(dǎo)入。

比如按需加載一個模塊可以這樣:

 
 
 
 
  1. if (xxx) { 
  2.     import('./module.js') 

import() 是異步導(dǎo)入的,結(jié)果會返回一個 Promise:

 
 
 
 
  1. import('/module.js') 
  2. .then((module) => { 
  3.     // Do something with the module. 
  4. }) 

動態(tài) import() 的應(yīng)用場景挺多的,比如 Vue 中的路由懶加載就是使用的動態(tài)導(dǎo)入組件。另外由于動態(tài)性不便于靜態(tài)分析工具和 tree-shaking 工作,所以不能濫用。

BigInt

BigInt 是一種內(nèi)置對象,它提供了一種方法來表示大于 - 1 的整數(shù)。這原本是 Javascript 中可以用 Number 表示的最大數(shù)字。BigInt 可以表示任意大的整數(shù)。

為了區(qū)分 Number,定義一個 BigInt 需要在整數(shù)后面加上一個 n,或者用函數(shù)直接定義:

 
 
 
 
  1. const num1 = 10n 
  2. const num2 = BigInt(20) 

Number 和 BigInt 之間能進行比較,但他們之間是寬松相等;且由于他們表示的是不同類型的數(shù)字,所以不能直接進行四則運算:

 
 
 
 
  1. 10n == 10         // true 
  2. 10n === 10        // false 
  3. 10n > 8           // true 
  4. 10 + Number(10n)  // 20 
  5. 10 + 10n          // TypeError 

Promise.allSettled

Promise.allSettled(iterable) 當(dāng)所有的實例都已經(jīng) settled,即狀態(tài)變化過了,那么將返回一個新實例,該新實例的內(nèi)部值是由所有實例的值和狀態(tài)組合成的數(shù)組,數(shù)組的每項是由每個實例的狀態(tài)和內(nèi)部值組成的對象。

 
 
 
 
  1. function init(){ 
  2.     return 3 
  3. let p1 = Promise.allSettled([ 
  4.     new Promise((resolve, reject) => { 
  5.         resolve(9) 
  6.     }).then(res => {}), 
  7.     new Promise((resolve, reject) => { 
  8.         reject(6) 
  9.     }), 
  10.     init() 
  11. ]) 
  12. let p2 = p1.then(res => { 
  13.     console.log(res) 
  14. }, err => { 
  15.     console.log(err) 
  16. }) 
  17. // [ 
  18. //      {status: "fulfilled", value: undefined},  
  19. //      {status: "rejected", reason: 6},  
  20. //      {status: "fulfilled", value: 3} 
  21. // ] 

只要所有實例中包含一個 pending 狀態(tài)的實例,那么 Promise.allSettled() 的結(jié)果為返回一個這樣 Promise { } 的實例。

globalThis

在以前,從不同的 JavaScript 環(huán)境中獲取全局對象需要不同的語句。在 Web 中,可以通過 window、self 或者 frames 取到全局對象,但是在 Web Workers 中,只有 self 可以。在 Node.js 中,它們都無法獲取,必須使用 global。

而現(xiàn)在只需要使用 globalThis 即可獲取到頂層對象,而不用擔(dān)心環(huán)境問題。

 
 
 
 
  1. // 在瀏覽器中 
  2. globalThis === window  // true 

import.meta

import.meta 是一個給 JavaScript 模塊暴露特定上下文的元數(shù)據(jù)屬性的對象。它包含了這個模塊的信息,比如說這個模塊的 URL,import.meta 必須在一個模塊里使用:

 
 
 
 
  1. // 沒有聲明 type="module",就使用 import.meta 會報錯 
  2.  
  3.  
  4. // 在module.js里 
  5. console.log(import.meta)   
  6. // {url: "http://localhost/3ag/js/module.js"} 

如果需要在配置了 Webpack 的項目,比如 Vue 里使用 import.meta 需要加一個包且配置一下參數(shù),否則項目編譯階段會報錯。

包配置詳情參考:

  • @open-wc/webpack-import-meta-loader

比如我用的是 4.x 版本的 vue-cli,那我需要在 vue.config.js 里配置:

 
 
 
 
  1. module.exports = { 
  2.     chainWebpack: config => { 
  3.         config.module 
  4.             .rule('js') 
  5.             .test(/\.js$/) 
  6.             .use('@open-wc/webpack-import-meta-loader') 
  7.                 .loader('@open-wc/webpack-import-meta-loader') 
  8.                 .end() 
  9.     } 

可選鏈操作符(?.)

通常我們獲取一個深層對象的屬性會需要寫很多判斷或者使用邏輯與 && 操作符,因為對象的某個屬性如果為 null 或者 undefined 就有可能報錯:

 
 
 
 
  1. let obj = { 
  2.     first: { 
  3.         second: '布蘭' 
  4.     } 
  5.  
  6. // 寫法一 
  7. let name1 = '' 
  8. if (obj) { 
  9.     if (obj.first) { 
  10.         name1 = obj.first.second 
  11.     } 
  12.  
  13. // 寫法二 
  14. let name2 = obj && obj.first && obj.first.second 

?. 操作符允許讀取位于連接對象鏈深處的屬性的值,而不必明確驗證鏈中的每個引用是否有效。如果某個屬性為 null 或者 undefined 則結(jié)果直接為 undefined。有了可選鏈操作符就可以使得表達式更加簡明了,對于上面例子用可選鏈操作符可以這么寫:

 
 
 
 
  1. let name3 = obj?.first?.second 

空值合并操作符(??)

對于邏輯或 || 運算符,當(dāng)對運算符左側(cè)的操作數(shù)進行裝換為 Boolean 值的時候,如果為 true,則取左邊的操作數(shù)為結(jié)果,否則取右邊的操作數(shù)為結(jié)果:

 
 
 
 
  1. let name = '' || '布蘭' 
  2. console.log(name)  // '布蘭' 

我們都知道 ''、0、null、undefined、false、NaN 等轉(zhuǎn)成 Boolean 值的時候都是 false,所以都會取右邊的操作數(shù)。這個時候如果要給變量設(shè)置默認值,如果遇到本身值就可能是 '' 或 0 的情況那就會出錯了,會被錯誤的設(shè)置為默認值了。

而 ?? 操作符就是為了解決這個問題而出現(xiàn)的,x ?? y 只有左側(cè)的操作數(shù)為 null 或 undefined 的時候才取右側(cè)操作數(shù),否則取左側(cè)操作數(shù):

 
 
 
 
  1. let num = 0 ?? 1 
  2. console.log(num)  // 0 

ES2021

如下這幾個提案已經(jīng)確定了會在 2021 年發(fā)布,所以把他們歸到 ES2021 中。

String.prototype.replaceAll

之前需要替換一個字符串里的全部匹配字符可以這樣做:

 
 
 
 
  1. const queryString = 'q=query+string+parameters' 
  2.  
  3. // 方法一 
  4. const withSpaces1 = queryString.replace(/\+/g, ' ') 
  5.  
  6. // 方法二 
  7. const withSpaces2 = queryString.split('+').join(' ') 

而現(xiàn)在只需要這么做:

 
 
 
 
  1. const withSpace3 = queryString.replaceAll('+', ' ') 

replaceAll 的第一個參數(shù)可以是字符串也可以是正則表達式,當(dāng)是正則表達式的時候,必須加上全局修
當(dāng)前題目:「建議收藏」送你一份精心總結(jié)的3萬字ES6實用指南(下)
文章起源:http://www.dlmjj.cn/article/dppcsho.html