關(guān)于es6-es11中新特性的一些想法

參考規(guī)范:https://tc39.es/ecma262/

Es6(es2015)

概述

  1. 增強(qiáng)功能 包括模塊、類聲明、詞法塊作用域、迭代器和生成器,異步編程,解構(gòu)模式以及適當(dāng)?shù)奈膊空{(diào)用。
  2. 內(nèi)置庫的擴(kuò)展 支持?jǐn)?shù)據(jù)抽象,包括Map, 集合Set, 以及二進(jìn)制數(shù)值的數(shù)組。對象以及數(shù)組功能的擴(kuò)展。

第六版的重點(diǎn)開發(fā)工作始于2009年,并于2015年6月通過,是經(jīng)過了15年的努力的結(jié)果。作為常規(guī)版本存在,對增量語言和庫增強(qiáng)功能提供了基礎(chǔ)。

因?yàn)镋s6已經(jīng)是大家耳熟能詳?shù)?,所以這里就不花篇幅了描述了,詳細(xì)見:es6教程

Es7(es2016)

在es6提出后,TC-39決議認(rèn)為耗時(shí)過長不利于可持續(xù)發(fā)展,于是將發(fā)布周期轉(zhuǎn)換為每年發(fā)布一個(gè)版本,以確保新的語言特性能夠更快的發(fā)展。

  1. Array.prototype.includes
  2. 新的指數(shù)運(yùn)算符 **
  3. 新的語法錯(cuò)誤

因?yàn)槊磕甓紩?huì)出一個(gè)版本,所以改動(dòng)不會(huì)像es6一樣那么大,一個(gè)個(gè)來看一下:

  1. Array.prototype.includes

    規(guī)定: 其實(shí)在es6中已經(jīng)有 Array.prototype.includes 了, 只不過仍未完善,所以出現(xiàn)在了es7的版本中。

    兩個(gè)參數(shù):第一個(gè)要搜索的值,第二個(gè)開始搜索的索引位置(可選,默認(rèn)為0)。

    返回值:找到返回true,找不到返回false。

    let nums = [7, 8, 9];
    
    // test
    console.log(nums.includes(7)); // true
    console.log(nums.includes(10)); // false
    console.log(nums.includes(7, 1)); // false
    

原因: 我們都知道在es6中存在多個(gè)類似功能的函數(shù),比如:

findIndex(function)、find(function)indexOf(value)

那么為什么還會(huì)有includes呢?

  • 首先,改名風(fēng)波:

    Array.prototype.includes 的前身提案名是Array.prototype.contains ,但是沒有正式推出,于是很多網(wǎng)站自行hack了。

    假如規(guī)范實(shí)現(xiàn)contains, 會(huì)出現(xiàn)contains 無法被for..in讀出來(因?yàn)閖s中所有原生提供的方法屬性都是不可枚舉的,只能通過屬性的屬性描述符獲取getOwnPropertyDescriptor(Array.prototype, 'indexOf')),而之前自行hack的contains的是可以被讀出來的。

    會(huì)出現(xiàn)代碼沒變動(dòng),在新規(guī)范推出后產(chǎn)生bug的情況。因此初稿階段,考慮新規(guī)范會(huì)使得許多現(xiàn)有網(wǎng)站出問題,所以改名includes。

  • 其次,雖然我們用indexOf可以模擬includes的行為,但出于語義描述不清楚的原因,所以要改。

    includes 表達(dá)了 是否包含該項(xiàng)的意思。

    indexOf 表達(dá)了查找數(shù)組中第一次出現(xiàn)對應(yīng)元素的索引是什么,在針對返回的索引進(jìn)一步處理的意思。

    has 為什么不叫has呢,因?yàn)閔as一般表示key中是否有(Map.prototype.has),includes表示value中是否有。

  • 那么他的判斷是否相等的方式是什么呢?答案是:SameValueZero ,es6中有4種相等算法

    • 松散相等比較:實(shí)現(xiàn)接口 ==

    • 嚴(yán)格相等比較:實(shí)現(xiàn)接口===,使用Array.prototype.indexOfArray.prototype.lastIndexOfcase-matching

    • SameValue: 實(shí)現(xiàn)接口Object.is()

    • SameValueZero: 實(shí)現(xiàn)接口借用了MapSet的方法。

      // 注意
      // SameValue 中 +0 和 -0 是false
      Object.is(-0, +0); // false
      // SameValueZero 中 +0 和 -0 是true 和Set的判斷很像不是
      const s = new Set();
      s.add(0);
      s.has(-0); // true
      

思考: 從探究整個(gè)提案的流程,可以看出一個(gè)新特性的出現(xiàn)需要考慮很多方面的問題。

類比之下,我們的項(xiàng)目在修改的時(shí)候 ,也需要考慮是否會(huì)對其他的東西產(chǎn)生影響,以及當(dāng)前的方案是否最優(yōu)。

思考一下,我們現(xiàn)在有用到查詢數(shù)組中是否包含某個(gè)元素的情況,我猜測大多數(shù)都是用的indexOf, 不過既然更優(yōu)的api,官方已經(jīng)提出來了 ,我們是否可以考慮換一下,畢竟可以減少碼量,同時(shí)語義更加清晰不是。

let arr = [1, 2, 3];

if (arr.indexOf(1) > -1) {...}
// ==> 等價(jià)于
if (arr.includes(1)) {...}
  1. 語法變化:新的指數(shù)運(yùn)算符**

    規(guī)定: 雖然Math.pow() 是已經(jīng)支持了指數(shù)運(yùn)算的方法,但是正式的運(yùn)算符** 看起來會(huì)會(huì)讓開發(fā)者更容易閱讀和理解。** 的左邊是基數(shù),右邊是指數(shù)。 如下:

    let result = 10 ** 2;
    
    console.log(result); // 100
    console.log(result === Math.pow(10, 2)) // true
    
    // 當(dāng)然 支持 **= 即 a = a**b <==> a **= b
    
    

原因: 沒錯(cuò) ,就是為了更容易書寫和閱讀。

思考: 官方都時(shí)刻關(guān)心,可見容易書寫和閱讀多么的重要,我以為我們的代碼可以更加的簡潔和易讀。推薦一個(gè)代碼規(guī)范地址:https://github.com/ryanmcdermott/clean-code-javascript

  1. 新的語法錯(cuò)誤

    規(guī)定:在參數(shù)被解構(gòu)或者有默認(rèn)參數(shù)的函數(shù)中,禁止使用嚴(yán)格模式"use strict"指令。否則會(huì)拋出錯(cuò)誤

    // 正確
    function good(first, second) { return first}
    
    // 拋出錯(cuò)誤 原因:有默認(rèn)參數(shù)并且使用了"use strict"
    function notGood(first, second = first) {
        "use strict";
        return first;
    }
    
    // 拋出錯(cuò)誤 原因:參數(shù)被解構(gòu)并且使用了"use strict"
    function notGood2({first, second}) {
        "use strict";
        return first;
    }
    

原因:實(shí)現(xiàn)運(yùn)行在嚴(yán)格模式下的參數(shù)非常困難,比如:

function foo(first = this) {
 "use strict";
    return first
}

在嚴(yán)格模式下first被認(rèn)為是undefined,可是參數(shù)的默認(rèn)值也可以是函數(shù)(通過apply , call指定上下文的調(diào)用方式),這就導(dǎo)致大多數(shù)的JavaScript引擎均不實(shí)現(xiàn)此功能,而是將其等同于全局對象。

思考:當(dāng)我們遇到一個(gè)不好實(shí)現(xiàn)或者解決的問題的時(shí)候,做個(gè)智者,未嘗不是一種明智的選擇(繞過它/拋異常)。

Es8(es2017)

  1. Object、String的靜態(tài)方法
  2. 在函數(shù)參數(shù)列表中尾隨逗號并調(diào)用
  3. 異步功能、共享內(nèi)存和原子

具體如下:

  1. Object、String的靜態(tài)方法

    規(guī)定: 擴(kuò)展了 Object和 String的功能

    • Object.values/Object.entries/Object.getOwnPropertyDescriptors()

      • Object.values接受一個(gè)對象并返回所有值構(gòu)成的數(shù)組,其順序和for … in 的順序相同。

      • Object.entries接受一個(gè)對象并返回?cái)?shù)組,每個(gè)元素都是一個(gè)包含兩個(gè)元素的數(shù)組。第一個(gè)是對象的key, 第二個(gè)是對象的value, 其順序和for … in 的順序相同。

      • Object.getOwnPropertyDescriptors 獲取屬性描述符,即對象所有自己的屬性描述符。包括value/writable/enumerable/configurable

        let obj = {
            one: 'hh',
            second: 'dd',
            thrid: 'zz'
        }
        Object.values(obj); // ["hh", "dd", "zz"]
        Object.entries(obj); // [["one", "hh"], ["second", "dd"], ["thrid", "zz"]]
        Object.getOwnPropertyDescriptor(obj, 'one'); // {value: "one", writable: true, enumerable: true, configurable: true}
        
    • String padding 用另一個(gè)字符串填充填充當(dāng)前字符串,直到結(jié)果字符串達(dá)到給定的長度為止,第二個(gè)參數(shù)默認(rèn)為空格。

      • String.padStart(長度, 填充字符串|' ') 從開頭填充, 填充字符從左往右填充

      • String.padEnd(長度, 填充字符串|' ') 從末尾向后填充,填充字符從左往右填充

        'abc'.padStart(10); // "       abc" 
        'abc'.padStart(10, 'foo'); // "foofoofabc"
        'abc'.padStart(6, '1234565'); // "123abc"
        'abc'.padStart(8, '0'); // '00000abc'
        'abc'.padStart(1); // 'abc'
        
        'abc'.padEnd(10); // "abc       " 
        'abc'.padEnd(10, 'foo');// "abcfoofoof"
        'abc'.padEnd(6, '123456'); // 'abc123'
        'abc'.padEnd(1); //  'abc'
        

思考:

Object.valuesObject.keys 對應(yīng)使用,

在對一些私密信息進(jìn)行處理的時(shí)候,需要將一些數(shù)字進(jìn)行加*隱藏,之前的寫法可能是進(jìn)行遍歷數(shù)組逐個(gè)替換,現(xiàn)在有了padStart,可以這樣試一下:

// 對身份進(jìn)行隱藏顯示
const fullNumber = '320321197803069527';

// old
const hideLen = fullNumber.length - 4;
let maskedNumber = ''
for (let i = 0; i < fullNumber.length; i++) {
    if (i < hideLen) {
     maskedNumber += '*';
    }else {
        maskedNumber += fullNumber[i]
    }
}
console.log(maskedNumber); // **************9527

// new 
const last4Digits = fullNumber.slice(-4);
const maskedNumberNew = last4Digits.padStart(fullNumber.length, '*'); 
console.log(maskedNumberNew); // **************9527 很簡潔,思路很清晰不是

還有一種情形就是 給定字符串長度,超出的以省略號表示 (這里不考慮超出指定長度省略號表示),試試看?

const str = 'Tom Jackson';
// 期望輸出: 'Tom...'
  1. 在函數(shù)參數(shù)列表中尾隨逗號并調(diào)用

    規(guī)定:在函數(shù)的參數(shù)列表中,最后一個(gè)參數(shù)以逗號結(jié)尾,如:

    function myFun(
     p1, 
        p2,
    ) {}
    myFun(
        'param1',
        'param2',
    )
    

    原因:

    1. 如果最后一項(xiàng)改變了位置,則不必添加和刪除逗號。
    2. 它可以幫助版本控制系統(tǒng)跟蹤實(shí)際更改的內(nèi)容。
  1. 異步功能

    規(guī)定: 異步功能,指的是定義一個(gè)異步函數(shù),也就是一個(gè)返回異步對象的函數(shù),返回Promise或者函數(shù)上加async標(biāo)記。

原因:當(dāng)異步功能有多個(gè)的時(shí)候,在一個(gè)函數(shù)中就會(huì)難以維護(hù),違背了單一職責(zé)原則, 就需要拆分出不同的方法,因此就有了異步功能。

思考: 異步功能很好的將業(yè)務(wù)封裝在了功能函數(shù)之中,調(diào)用者只需要關(guān)心成功和失敗兩種狀態(tài)(resolve和reject),這樣會(huì)使得代碼更具有可讀性。

這種場景目前用的最多的是在接口函數(shù)的編寫中,它讓我們不用太過關(guān)心接口的發(fā)送以及對返回?cái)?shù)據(jù)的處理,只需要使用即可。

  1. 共享內(nèi)存和原子

    規(guī)定: 引入新的構(gòu)造函數(shù) SharedArrayBuffer 和 具有輔助函數(shù)的命名空間對象 Atomics, 用原子操作進(jìn)行通信的程序,即使在并行CPU上也能確保定義良好的執(zhí)行順序。全局變量 Atomics,它的方法具有三個(gè)主要用例。

    • 同步化
    • 等待通知
    • 原子操作
    Atomics.store() // 寫入
    Atomics.load() // 讀取
    Atomics.wait() // 等待
    Atomics.wake() // 喚醒
    

    原因: 雖然用到的地方很少,但是作為一門語言,對于并行操作還是要支持的。Web workers 將任務(wù)并行引入了 JavaScript 。還記得PWA嗎?它的核心就在與本地開啟了一個(gè)service worker,并對數(shù)據(jù)進(jìn)行了緩存以及代理。

    思考: 要實(shí)現(xiàn)并行,需要滿足兩點(diǎn):一個(gè)是數(shù)據(jù)的并行,一個(gè)是代碼的并行。這里SharedArrayBuffer作為共享陣列緩沖區(qū)是更高抽象的基本構(gòu)建塊。 它允許多個(gè)workers和主線程之間共享SharedArrayBuffer對象的字節(jié)。同時(shí)Atomics的 方法可以用來與其他 workers 進(jìn)行同步。

    詳情可以了解https://www.html.cn/archives/7724

Es9(es2018)

  1. 解除模板字面量限制
  2. 正則表達(dá)式相關(guān)
  3. 對象解構(gòu)中的rest操作符(...)/對象字面量中的spread操作符(...)
  4. Promise.prototype.finally 和 異步的迭代

具體如下:

  1. 解除模版字面量的限制

    規(guī)定: 對于模版字面量中,如果存在非法轉(zhuǎn)義字符 ,則將模版字面量設(shè)置為undefined。不過可以通過.raw訪問原始值。

    function tag(strs) {
        console.log(strs[0] === undefined); 
        console.log(strs.raw[0] === '\\unicode and \\u{55}')
    }
    tag`\unicode and \u{55}`; // true true
    
    const badSequence = `bad escape sequence: \unicode`; // Uncaught SyntaxError: Invalid Unicode escape sequence
    

    原因:解決模版字面量中存在非法轉(zhuǎn)義字符報(bào)錯(cuò)的問題。

    思考:在處理字符串的時(shí)候,我們多是需要注意處理需要進(jìn)行轉(zhuǎn)義的字符,此時(shí)有了這個(gè)特性 ,使的我們可以封裝個(gè)公共的方法,放心的使用模版字面量,沒必要進(jìn)行而外的處理了。

  1. 正則表達(dá)式相關(guān)

    • . 的使用

      規(guī)定: es9支持了/s模式下,. 可以匹配換行符。

      console.log(/./s.test("\n")); //  true
      console.log(/./s.test("\r")); //  true
      

      原因:正則中.看似可以匹配任何字符,但換行符除外,當(dāng)時(shí)大家用[\w\W]替代了.。這個(gè)終究是個(gè)缺陷。于是增加了在 /s 模式下,. => [\w\W]。

  • 對捕獲組進(jìn)行命名:

    規(guī)定: 使用捕獲組需要通過指定捕獲組名稱來進(jìn)行引用,每個(gè)名稱是唯一的,避免沖突。語法:?<name>

    const regExp = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
    const result = regExp.exec('2020-10-21');
    console.log(result.groups.year === '2020'); //true => result[1] === '2020'
    console.log(result.groups.month === '10'); //true => result[2] === '10'
    console.log(result.groups.day === '21');//true => result[3] === '21'
    // 其中 result[0] === '2020-10-21' 
    
    // 命名捕獲組這塊 建議結(jié)合解構(gòu)字面量一起使用 會(huì)更加清晰
    const { groups: { year, month, day } } = regExp.exec('2020-10-21');
    

    原因:對捕獲組命名可以讓調(diào)用更加清晰。

  • 支持了后行斷言:

    規(guī)定: es9之前只支持先行斷言,現(xiàn)在支持了后行斷言。

    正向后行斷言:(?<=...) 表示之前的字符串能匹配

    const re = /(?<=\d{3}) meters/; // 表示 meters 之前 匹配三個(gè)數(shù)字
    console.log(re.test('10 meters')); // false
    console.log(re.test('100 meters')); // true
    

    負(fù)向后行斷言:(?<!...) 表示之前的字符串不能匹配

    const re = /(?<!\d{3}) meters/; // 表示 meters 之前 不能匹配三個(gè)數(shù)字
    console.log(re.test('10 meters')); // true
    console.log(re.test('100 meters')); // false
    

    原因: 彌補(bǔ)之前只能支持先行斷言的缺陷。

    思考: 正則中完整的斷言定義:正/負(fù)向斷言 與 先/后行斷言 的笛卡爾積組合(互相組合得到4種結(jié)果)。

    補(bǔ)充一下正向先行斷言負(fù)向先行斷言

    // 正向先行斷言
    const re = /meters(?= 10)/;
    console.log(re.test('meters 10')); // true
    console.log(re.test('meters 5')); // false
    
    // 負(fù)向先行斷言
    const re2 = /meters(?! 10)/; 
    console.log(re.test('meters 10')); // false
    console.log(re.test('meters 5')); // true
    
  • Unicode 屬性轉(zhuǎn)義

    規(guī)定:正則支持了更加強(qiáng)大的unicode匹配方式。在/u(修飾符u指unicode)模式下,通過\p{}大括號內(nèi)提及unicode字符屬性來匹配字符。

    可以用\p{Number}匹配所有數(shù)字

    可以用\p{Alphabetic}匹配所有Alphabetic元素,包括漢字,字母等

    可以用\p{White_Space}匹配空格、制表符、換行符。

    等等。

    const regex = /^\p{Number}+$/u;
    regex.test("231???"); // true
    regex.test("???"); // true
    regex.test("ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ"); // true
    
    const str = "漢";
    console.log(/\p{Alphabetic}/u.test(str)); //  true
    // the \w shorthand cannot match 漢
    console.log(/\w/u.test(str)); // false
    
    /^\p{White_Space}+$/u.test('\n \r \t'); // true
    

    原因: 創(chuàng)建支持unicode的正則表達(dá)式不再麻煩,更加易讀。使用Unicode屬性轉(zhuǎn)義符的代碼將保持“最新”狀態(tài):每當(dāng)Unicode標(biāo)準(zhǔn)得到更新時(shí),ECMAScript引擎就會(huì)更新其數(shù)據(jù)。

  1. 對象解構(gòu)中的rest操作符(...)/對象字面量中的spread操作符(...)

    // 對象解構(gòu)中的rest操作符(...)
    const {one, two, ...others} = { one: 'one', two: 'two', three: 'three', four: 'four'};
    console.log(others); // { three: 'three', four: 'four' }
     
    // 對象字面量中的spread操作符(...)
    const nums = {one, two, ...others};
    console.log(nums);// { one: 'one', two: 'two', three: 'three', four: 'four'};
    

    **思考: **需要注意的是 在使用對象字面量的時(shí)候 如果有key重復(fù),那么對象中后面的key的值會(huì)覆蓋前面的。

  2. Promise.prototype.finally 和 異步的迭代

    • Promise.prototype.finally

      規(guī)定:一旦執(zhí)行了Promise的resolve回調(diào)或者reject回調(diào),最終都會(huì)執(zhí)行finally回調(diào)

      思考:總有一些相同的處理需要在resolve回調(diào)或者reject回調(diào)后處理,比如:資源的回收或者網(wǎng)絡(luò)請求時(shí)loading的狀態(tài)修改等。 平時(shí)我們會(huì)在resolve回調(diào)或者reject回調(diào)中都添加或者提取一個(gè)公共方法?,F(xiàn)在會(huì)讓我們更省力和美觀。

    • 異步的迭代

      規(guī)定:這個(gè)是for...of的一種變體,他允許在由異步組成的可迭代對象上進(jìn)行迭代。

      async function asyncFun() {
          const promises = [
              fetch('f1.json'),
             fetch('f2.json'),
              fetch('f3.json'),
          ];
          // 正常的迭代
          for (const promiseItem of promises) {
              console.log(item); // =>  輸出 promise
         }
          
          // 正常的迭代
          for await (const promiseItem of promises) {
              console.log(item); // =>  輸出 resolved response
         }
      }
      

      思考: 適合的場景會(huì)是 多個(gè)異步同步執(zhí)行,并對每個(gè)結(jié)果有統(tǒng)一的處理方式。比如:對文件的處理。

Es10(es2019)

  1. Array.prototype.flat 和 Array.prototype.flatMap

  2. Object.fromEntries

  3. String.prototype.trimStart, String.prototype.trimEnd

  4. Symbol的描述訪問器

  5. try { } catch {} 可省略catch參數(shù)的寫法

  6. 對行分隔符\u2028和段落分隔符\u2029的支持,

  7. JSON.stringify對特殊字符的轉(zhuǎn)義處理

  8. Array.prototype.sort

  9. Function.prototype.toString

具體如下:

  1. Array.prototype.flat 和 Array.prototype.flatMap

    規(guī)定:

    Array.prototype.flat(Number) ,降緯度數(shù)組,具體下降多少維度視參數(shù)而定,知道無法繼續(xù)降維為止。默認(rèn)為1

    Array.prototype.flatMap(Function) ,flat和map功能的組合,接受一個(gè)函數(shù), 返回一個(gè)處理后的降維了1個(gè)的數(shù)組。

    const fruits = [['apple', 'banana'], ['蘋果', '香蕉']];
    console.log(fruits.flat()); // ['apple', 'banana', '蘋果', '香蕉']
    
    const fruits2 = [['apple'], ['banana'], ['蘋果'], ['香蕉']];
    const mappedAndFlatten = fruits2.flatMap((fruit, index) => [fruit + '_fruit']);
    console.log(mappedAndFlatten); // ["apple_fruit", "banana_fruit", "蘋果_fruit", "香蕉_fruit"]
    

    思考:

    對比我們平常使用的解構(gòu)方法, 需要遞歸遍歷:

    function reduceDimension(arr){
        let ret = [];
        let targetArr = function(arr){
            arr.forEach(function(item){
                item instanceof Array ? targetArr(item) : ret.push(item);
            });
        }
        targetArr(arr);
        return ret;
    }
    
    // 等同于 arr.flat(Infinity)
    

    發(fā)現(xiàn) flat(Infinity) 無疑更簡潔(Infinity 作為數(shù)值中的無限大,可以保證數(shù)組能夠完全解構(gòu))。

  1. Object.fromEntries

    規(guī)定: 將 由鍵值對組成的數(shù)組轉(zhuǎn)換成一個(gè)對象, 與 Object.entries功能相反。

    const fruits = { apple: '蘋果', banana: '香蕉', orange: '橘子'};
    const entries = Object.entries(fruits);// [["apple", "蘋果"],["banana", "香蕉"] ,["orange", "橘子"]]
    const fromEntries = Object.fromEntries(entries); // { apple: '蘋果', banana: '香蕉', orange: '橘子'}
    

    思考: 這是針對這種特定數(shù)據(jù)結(jié)構(gòu)的格式化 函數(shù)。對于一個(gè)對象如果需要同時(shí)修改對象的key-value, 直接修改不方便,我們可以先將一個(gè)對象通過Object.entries轉(zhuǎn)換成數(shù)組,進(jìn)行修改,完成后通過Object.fromEntries轉(zhuǎn)換回來。

  1. String.prototype.trimStart, String.prototype.trimEnd

    • trimStart :去除字符串前面的空格

    • trimEnd: 去除字符串后面的空格

  1. Symbol的描述訪問器

    規(guī)定: 通過 Symbol.prototype.description可以獲取定義Symbol時(shí)的具體數(shù)值

    const OPEN = Symbol('STATUS_OPEN');
    // old 可以通過 toString() 來獲取描述信息
    console.log(OPEN.toString()); // "Symbol('STATUS_OPEN')"
    
    // new 通過 description屬性獲取
    console.log(OPEN.description); // "STATUS_OPEN"
    

    思考: Symbol的出現(xiàn) 讓我們在常量的使用中大量使用,不過有時(shí)侯需要獲取它的描述值來做一些判斷。

    const BANNER = {
        LEFT: Symbol('left'),
        RIGHT: Symbol('right'),
    }
    
    // 使用時(shí)候
    if (BANNER.LEFT.description === 'left') {
        // do something
    }
    
  1. try { } catch {} 可省略catch參數(shù)的寫法

    規(guī)定: catch后面的括號以及參數(shù)在不用的時(shí)候可以省略

    try { } catch(err) {}
    try { } catch {}  // 不會(huì)報(bào)錯(cuò)
    

    思考:有些時(shí)候我們不需要對catch 的err 數(shù)據(jù)進(jìn)行操作,這個(gè)時(shí)候如果傳遞了,但是未使用,那么eslint 可能會(huì)警告我們代碼中存在可優(yōu)化。本著不用不寫的原則,省略無疑是很好的一種方式。

  1. 對行分隔符\u2028和段落分隔符\u2029的支持

    規(guī)定: es10之前 '\u2028''\u2020'這種寫法會(huì)報(bào)錯(cuò),現(xiàn)在不會(huì)啦。

  1. JSON.stringify對特殊字符的轉(zhuǎn)義處理

    規(guī)定: JSON.stringify 在處理\uD800\DFFF之間的字符,如果找不到對應(yīng)的,直接返回轉(zhuǎn)義后的字符。

    JSON.stringify('\uDF06\uD834'); // '"\\udf06\\ud834"'
    
  1. Array.prototype.sort

    規(guī)定: es10之前 V8對于包含10個(gè)以上的數(shù)組排序時(shí)采用了不穩(wěn)定的快速排序算法,這個(gè)算法可能會(huì)改變原來數(shù)組里面相同數(shù)值的元素的先后關(guān)系。 通過采用原地排序保證數(shù)組中相同數(shù)值元素的先后關(guān)系。避免了順序被改變的問題。

  1. Function.prototype.toString

    規(guī)定:返回一個(gè)表示函數(shù)源代碼的字符串,包括格式和注釋,比較精確。

    function add(num1, num2) {
        /* add */
        return num1 + num2;
    }
    
    add.toString(); 
    // "function add(num1, num2) {
    //    /* add */
    //    return num1 + num2;
    // }" 
    

Es11(es2020)

  1. String.prototype.matchAll

  2. Promise.allSettled

  3. export * as ns from 'module'

  4. for-in 對于迭代對象順序的統(tǒng)一

  5. 鏈合并運(yùn)算符 ?. 以及 Null判斷運(yùn)算符 ??

  6. 新增原始類型 BigInt ,可以表示任意精度的整數(shù)

  7. import 動(dòng)態(tài)導(dǎo)入

  8. import.meta

  9. 標(biāo)準(zhǔn)化的全局對象

具體如下:

  1. String.prototype.matchAll

    規(guī)定: 返回所有與正則表達(dá)式相匹配字符串的結(jié)果的迭代器,包括捕獲組。相比于match, 它提供的信息更全。

    const regexp = /t(e)(st(\d?))/g;
    const str = 'test1test2';
    
    // 曾經(jīng) 我們通過 while 和 exec配合來獲取比較全的匹配信息
    let result = null;
    while((result = regexp.exec(str)) !== null) {
     console.log(result);
    }
    // >  ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined]   
    // >  ["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined]
    
    // 現(xiàn)在 我們用matchAll 就好了
    const array = [...str.matchAll(regexp)];
    for (result of array) {
        console.log(result);
    }
    // >  ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined]   
    // >  ["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined]
    
  1. Promise.allSettled

    規(guī)定:對比Promise.all 方法,Promise.allSettled 當(dāng)每個(gè)Promise 無論失敗還是成功都會(huì)走回調(diào)函數(shù)。

    const promiseArr = [
        Promise.resolve(100),
        Promise.reject(null),
        Promise.reject(new Error('Oh No')),
    ]
    
    Promise.all(promiseArr).then(results => {
        // 不會(huì)執(zhí)行
        console.log('All Promise Settled', results);
    });
    
    Promise.allSettled(promiseArr).then(results => {
        console.log('All Promise Settled', results);
    });
    // [{status: "fulfilled", value: 100},
    // {status: "rejected", reason: null},
    // {status: "rejected", reason: Error: Oh No at <anonymous>:4:20}]
    

    思考:當(dāng)我們在進(jìn)行多個(gè)接口請求的時(shí)候,如果通過Promise.all ,不能保證回調(diào)一定會(huì)執(zhí)行,這時(shí)候如果有l(wèi)oading狀態(tài)的,無疑會(huì)出現(xiàn)問題,無法在回調(diào)中更新。如果我們并不關(guān)心接口是否調(diào)用成功,這個(gè)時(shí)候Promise.allSettled 無疑是更合適的。

  1. export * as ns from 'module'

    規(guī)定: 在一個(gè)文件中引用另外一個(gè)文件并導(dǎo)出他的一種縮寫。

    export * as utils from './utils.mjs'
    
    // 等價(jià)于 ==> 
    import * as utils from './utils.mjs'
    export { utils }
    

    思考: 在react中使用組件的時(shí)候,我們會(huì)對一個(gè)目錄下的多個(gè)文件 通過index.js導(dǎo)出。好處在于當(dāng)前文件中并不會(huì)真的導(dǎo)入文件對象的引用,比如以上的utils為undefined。

    // index.js old
    import * as Header from './header.jsx'
    import * as Footer from './footer.jsx'
    // ...
    export { Header }
    export { Footer }
    // ...
    
    // index.js new 
    export * as Header from './header.jsx';
    export * as Footer from './footer.jsx';
    export * as Body from './body.jsx';
    export * as Nav from './nav.jsx';
    
  1. for-in 對于迭代對象順序的統(tǒng)一

    規(guī)定:雖然瀏覽器自己保持了 for in的順序,但是在es11中進(jìn)行了標(biāo)準(zhǔn)化。

  1. 鏈合并運(yùn)算符 ?. 以及 Null判斷運(yùn)算符 ??

    規(guī)定:鏈合并運(yùn)算符 避免了調(diào)用時(shí)左側(cè)的對象是否為null或undefined的判斷,Null判斷運(yùn)算符 為屬性值為null或undefined時(shí),指定默認(rèn)值

    const userName = (list && list.info 
                      && list.info.base 
                      && list.info.base.userName
                     ) || 'userName';
    // => 等價(jià)于
    const userName = list?.info?.base?.userName ?? 'userName';
    

    思考: ?? 類似于 || ,區(qū)別在于:

    • ?? 只當(dāng)左側(cè)為 null/undefined的時(shí)候才會(huì)指定右側(cè)默認(rèn)值

    • || 當(dāng)左側(cè)所有轉(zhuǎn)換后boolean值為false 的時(shí)候會(huì)指定右側(cè)默認(rèn)值(包括 0 )

      在某些情況下 我們并不需要這種結(jié)果,因此結(jié)合業(yè)務(wù)考慮使用哪一種。

  1. 新增原始類型 BigInt ,可以表示任意精度的整數(shù)

    規(guī)定: 顧名思義,就是用來存放Number放不下的數(shù)字

    let maxNum = 9007199254740992; // Number的最大值 pow(2, 53) - 1
    console.log(maxNum + 1); // 9007199254740992
    
    maxNum = 9007199254740992n;
    console.log(maxNum + 1n); // 9007199254740993n
    console.log(typeof maxNum === 'bigint'); // true
    console.log(maxNum + 1); // Uncaught TypeError: Cannot mix BigInt and other types 
    

思考: 在進(jìn)行大數(shù)計(jì)算的時(shí)候,之前需要通過豎式運(yùn)算,一種從個(gè)位往前一個(gè)一個(gè)相加求和的方式,比較麻煩?,F(xiàn)在直接用BigInt就可以了

  1. import 動(dòng)態(tài)導(dǎo)入

    規(guī)定: 返回所請求模塊的模塊名稱空間對象的Promise,所以也可以使用async/await

    const modulePath = './file.js';
    import(modulePath)
     .then((module) => {
         // do something
     });
    // or
    (async function() {
        const modulePath = './file.js';
        const module = await import(modulePath);
        // do somthing
    })();
    

    思考:import 的動(dòng)態(tài)導(dǎo)入支持 一方面可以讓我們按需加載,另一方面的可能會(huì)出現(xiàn)濫用的可能,對靜態(tài)文件分析造成一定程度上的阻礙(比如:tree-shaking 就是依賴于 import的靜態(tài)導(dǎo)入,如果都做成了動(dòng)態(tài)導(dǎo)入,那么該功能的能力會(huì)收到很大的影響)。

  1. import.meta

    規(guī)定: 可以通過import.meta獲取module的信息

    <script type="module" src="module.js"></script>
    
    // module.js
    console.log(import.meta) // 得到一個(gè)對象 包含模塊的信息{ url: "file:///home/user/module.js" }
    

    思考:在使用一個(gè)模塊時(shí),有時(shí)需要知道模板本身的一些信息(比如模塊的路徑)。這是import.meta的用處。

  1. 標(biāo)準(zhǔn)化的全局對象

    規(guī)定:全局變量之前在各個(gè)平臺未統(tǒng)一,需要自行標(biāo)準(zhǔn)化。

    var getGlobal = function () {
        if (typeof self !== 'undefined') { return self; }
        if (typeof window !== 'undefined') { return window; }
        if (typeof global !== 'undefined') { return global; }
        throw new Error('unable to locate global object');
    }
    
    // getGlobal() 等同于 globalThis
    

其他可能的提案

  1. do表達(dá)式

  2. 雙冒號運(yùn)算符

  3. 管道運(yùn)算符

  4. #!命令

具體如下:

  1. do表達(dá)式

    規(guī)定: 通過在塊級作用域前添加do , 讓塊級作用域可以變?yōu)楸磉_(dá)式。也就是說可以返回值。do 也提供了單獨(dú)的作用域。do表達(dá)式的邏輯:封裝什么就會(huì)返回什么

    let x = do {
      let t = f();
      t * t + 1;
    };
    // x的值等于 t*t+1的結(jié)果
    
    // 等同于 <表達(dá)式>
    do { <表達(dá)式>; }
    // 等同于 <語句>
    do { <語句> }
    

    思考:我們在寫jsx的時(shí)候,do很好用。以下避免了三元運(yùn)算符的使用,同時(shí)代碼意圖比較清晰。

    return (
      <nav>
        <Home />
        {
          do {
            if (loggedIn) {
              <LogoutButton />
            } else {
              <LoginButton />
            }
          }
        }
    
      </nav>
    )
    
  1. 雙冒號運(yùn)算符

    規(guī)定:即“函數(shù)綁定”(function bind)運(yùn)算符,用來取代call、apply、bind調(diào)用。左邊是一個(gè)對象,右邊是一個(gè)函數(shù)

    foo::bar;
    // 等同于
    bar.bind(foo);
    
    foo::bar(...arguments);
    // 等同于
    bar.apply(foo, arguments)    
    
  2. 管道運(yùn)算符

    規(guī)定:JavaScript 的管道是一個(gè)運(yùn)算符,寫作|>。它的左邊是一個(gè)表達(dá)式,右邊是一個(gè)函數(shù)。支持await。

    x |> f
    // 等同于
    f(x)
    
    x |> await f
    // 等同于
    await f(x)
    

    思考:管道運(yùn)算符最大的好處,就是可以把嵌套的函數(shù),寫成從左到右的鏈?zhǔn)奖磉_(dá)式。

    function doubleSay (str) {
      return str + ", " + str;
    }
    function capitalize (str) {
      return str[0].toUpperCase() + str.substring(1);
    }
    function exclaim (str) {
      return str + '!';
    }
    
    let msg = 'hello';
    const result = msg |> doubleSay |> capitalize |> exclaim;
    // 等同于
    const resultNew = exclaim(capitalize(doubleSay(msg)));
    
  1. #!命令

    規(guī)定: JavaScript 腳本引入了#!命令,寫在腳本文件或者模塊文件的第一行。Unix 命令行就可以直接執(zhí)行腳本。同時(shí)對于JavaScript 引擎來說,會(huì)把#!理解成注釋,忽略掉這一行。

    // 寫在腳本文件第一行 hello.js
    #!/usr/bin/env node
    'use strict';
    console.log(1);
    
    # 以前執(zhí)行腳本的方式
    $ node hello.js
    # 現(xiàn)在執(zhí)行 的方式
    $ ./hello.js
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容