1. 即將到來的 ECMAScript 2022 新特性

        共 16949字,需瀏覽 34分鐘

         ·

        2022-02-13 01:09


        ECMAScript 規(guī)范每年都會更新一次,正式標準化 JavaScript 語言的 ECMAScript 的下一次年度更新將在 2022 年 6 月左右獲得批準。自 2015 年以來,TC39 團隊成員每年都會一起討論可用的提案,并發(fā)布已接受的提案。今年是 ECMAScript 的第 13 版,其中包括許多實用的功能。所有在 2022 年 3 月之前達到階段 4 的提案都將包含在全新的 ECMAScript 2022 標準中。

        對于一個提案,從提出到最后被納入ES新特性,TC39的規(guī)范中分為五步:

        • stage0(strawman),任何TC39的成員都可以提交。
        • stage1(proposal),進入此階段就意味著這一提案被認為是正式的了,需要對此提案的場景與API進行詳盡的描述。
        • stage2(draft),演進到這一階段的提案如果能最終進入到標準,那么在之后的階段都不會有太大的變化,因為理論上只接受增量修改。
        • state3(candidate),這一階段的提案只有在遇到了重大問題才會修改,規(guī)范文檔需要被全面的完成。
        • state4(finished),這一階段的提案將會被納入到ES每年發(fā)布的規(guī)范之中

        目前,一些提案還處于第三階段,一些提案已經(jīng)進入第四階段。提案的功能將在達到第 4 階段后被添加到新的ECMAScript標準中,這意味著它們已獲得 TC-39 的批準,通過了測試,并且至少有兩個實現(xiàn)。下面就來看看 ECMAScript 2022 預(yù)計會推出的新功能吧~

        一、Top-level Await

        在ES2017中引入了 async 函數(shù)和 await 關(guān)鍵字,以簡化 Promise 的使用,但是 await 關(guān)鍵字只能在 async 函數(shù)內(nèi)部使用。嘗試在異步函數(shù)之外使用 await 就會報錯:SyntaxError - SyntaxError: await is only valid in async function。

        頂層 await 允許我們在 async 函數(shù)外面使用 await 關(guān)鍵字,目前提案正處于第 4 階段,并且已經(jīng)在三個主要的瀏覽器的 JavaScript 引擎中實現(xiàn),模塊系統(tǒng)會協(xié)調(diào)所有的異步 promise。

        頂層 await 允許模塊充當大型異步函數(shù),通過頂層 await,這些ECMAScript模塊可以等待資源加載。這樣其他導(dǎo)入這些模塊的模塊在執(zhí)行代碼之前要等待資源加載完再去執(zhí)行。下面來看一個簡單的例子。

        由于 await 僅在 async 函數(shù)中可用,因此模塊可以通過將代碼包裝在 async 函數(shù)中來在代碼中包含 await:

        ??//?a.js
        ??import?fetch??from?"node-fetch";
        ??let?users;

        ??export?const?fetchUsers?=?async?()?=>?{
        ????const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');
        ????users?=??resp.json();
        ??}
        ??fetchUsers();

        ??export?{?users?};
        ??//?usingAwait.js
        ??import?{users}?from?'./a.js';
        ??console.log('users:?',?users);
        ??console.log('usingAwait?module');

        我們還可以立即調(diào)用頂層async函數(shù)(IIAFE):

        import?fetch??from?"node-fetch";
        ??(async?()?=>?{
        ????const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');
        ????users?=?resp.json();
        ??})();
        ??export?{?users?};

        這樣會有一個缺點,直接導(dǎo)入的 users 是 undefined,需要在異步執(zhí)行完成之后才能訪問它:

        //?usingAwait.js
        import?{users}?from?'./a.js';

        console.log('users:',?users);?//?undefined

        setTimeout(()?=>?{
        ??console.log('users:',?users);
        },?100);

        console.log('usingAwait?module');

        當然,這種方法并不安全,因為如果異步函數(shù)執(zhí)行花費的時間超過100毫秒, 它就不會起作用了,users 仍然是 undefined。

        另一個方法是導(dǎo)出一個 promise,讓導(dǎo)入模塊知道數(shù)據(jù)已經(jīng)準備好了:

        //a.js
        import?fetch??from?"node-fetch";
        export?default?(async?()?=>?{
        ??const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');
        ??users?=?resp.json();
        })();
        export?{?users?};

        //usingAwait.js
        import?promise,?{users}?from?'./a.js';
        promise.then(()?=>?{?
        ??console.log('usingAwait?module');
        ??setTimeout(()?=>?console.log('users:',?users),?100);?
        });

        雖然這種方法似乎是給出了預(yù)期的結(jié)果,但是有一定的局限性:導(dǎo)入模塊必須了解這種模式才能正確使用它。

        而頂層await就可以消除這些缺點:

        ??//?a.js
        ??const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');
        ??const?users?=?resp.json();
        ??export?{?users};

        ??//?usingAwait.js
        ??import?{users}?from?'./a.mjs';

        ??console.log(users);
        ??console.log('usingAwait?module');

        頂級 await 在以下場景中將非常有用:

        • 動態(tài)加載模塊
        ?const?strings?=?await?import(`/i18n/${navigator.language}`);???
        • 資源初始化
        const?connection?=?await?dbConnector();???
        • 依賴回退
        let?translations;
        try?{
        ??translations?=?await?import('https://app.fr.json');
        }?catch?{
        ??translations?=?await?import('https://fallback.en.json');
        }

        目前,在這些地方已經(jīng)支持 Top-level await:

        • V8 v8.9
        • Webpack 5.0.0
        • Babel
        • Chrome DevTools REPL
        • Node REPL

        二、類的實例成員

        1. 公共實例字段

        公共類字段允許我們使用賦值運算符 (=) 將實例屬性添加到類定義中。下面來一個計數(shù)器的例子:

        import?React,?{?Component?}?from?"react";

        export?class?Incrementor?extends?Component?{
        ??constructor()?{
        ????super();
        ????this.state?=?{
        ??????count:?0,
        ????};
        ????this.increment?=?this.increment.bind(this);
        ??}

        ??increment()?{
        ????this.setState({?count:?this.state.count?+?1?});
        ??}

        ??render()?{
        ????return?(
        ??????<button?onClick={this.increment}>Increment:?{this.state.count}button>
        ????);
        ??}
        }

        在這個例子中,在構(gòu)造函數(shù)中定義了實例字段和綁定方法,通過新的類語法,我們可以使代碼更加直觀。新的公共類字段語法允許我們直接將實例屬性作為屬性添加到類上,而無需使用構(gòu)造函數(shù)方法。這樣就簡化了類的定義,使代碼更加簡潔、可讀:

        import?React?from?"react";

        export?class?Incrementor?extends?React.Component?{
        ??state?=?{?count:?0?};

        ??increment?=?()?=>?this.setState({?count:?this.state.count?+?1?});

        ??render?=?()?=>?(
        ????<button?onClick={this.increment}>Increment:?{this.state.count}button>
        ??);
        }

        有些小伙伴可能就疑問了,這個功能很早就可以使用了呀。但是它現(xiàn)在還不是標準的 ECMAScript,默認是不開啟的,如果使用 create-react-app 創(chuàng)建 React 項目,那么它默認是啟用的,否則我們必須使用正確的babel插件才能正常使用(@babel/preset-env)。

        下面來看看關(guān)于公共實例字段的注意事項:

        • 公共實例字段存在于每個創(chuàng)建的類實例上。它們要么是在Object.defineProperty()中添加,要么是在基類中的構(gòu)造時添加(構(gòu)造函數(shù)主體執(zhí)行之前執(zhí)行),要么在子類的super()返回之后添加:
        class?Incrementor?{
        ??count?=?0
        }

        const?instance?=?new?Incrementor();
        console.log(instance.count);?//?0
        • 未初始化的字段會自動設(shè)置為 undefined:
        class?Incrementor?{
        ??count
        }

        const?instance?=?new?Incrementor();
        console.assert(instance.hasOwnProperty('count'));
        console.log(instance.count);??//?undefined
        • 可以進行字段的計算:
        const?PREFIX?=?'main';

        class?Incrementor?{
        ??[`${PREFIX}Count`]?=?0
        }

        const?instance?=?new?Incrementor();
        console.log(instance.mainCount);???//?0

        2. 私有實例字段、方法和訪問器

        默認情況下,ES6 中所有屬性都是公共的,可以在類外檢查或修改。下面來看一個例子:

        class?TimeTracker?{
        ??name?=?'zhangsan';
        ??project?=?'blog';
        ??hours?=?0;

        ??set?addHours(hour)?{
        ????this.hours?+=?hour;
        ??}

        ??get?timeSheet()?{
        ????return?`${this.name}?works?${this.hours?||?'nothing'}?hours?on?${this.project}`;
        ??}
        }

        let?person?=?new?TimeTracker();
        person.addHours?=?2;?//?標準?setter
        person.hours?=?4;????//?繞過?setter?進行設(shè)置
        person.timeSheet;?

        可以看到,在類中沒有任何措施可以防止在不調(diào)用 setter 的情況下更改屬性。

        而私有類字段將使用哈希#前綴定義,從上面的示例中,我們可以修改它以包含私有類字段,以防止在類方法之外更改屬性:

        class?TimeTracker?{
        ??name?=?'zhangsan';
        ??project?=?'blog';
        ??#hours?=?0;??//?私有類字段

        ??set?addHours(hour)?{
        ????this.#hours?+=?hour;
        ??}

        ??get?timeSheet()?{
        ????return?`${this.name}?works?${this.#hours?||?'nothing'}?hours?on?${this.project}`;
        ??}
        }

        let?person?=?new?TimeTracker();
        person.addHours?=?4;?//?標準?setter
        person.timeSheet?????//?zhangsan?works?4?hours?on?blog

        當我們嘗試在 setter 方法之外修改私有類字段時,就會報錯:

        person.hours?=?4?//?Error?Private?field?'#hours'?must?be?declared?in?an?enclosing?class

        我們還可以將方法或 getter/setter 設(shè)為私有,只需要給這些方法名稱前面加#即可:

        class?TimeTracker?{
        ??name?=?'zhangsan';
        ??project?=?'blog';
        ??#hours?=?0;???//?私有類字段

        ??set?#addHours(hour)?{
        ????this.#hours?+=?hour;
        ??}

        ??get?#timeSheet()?{
        ????return?`${this.name}?works?${this.#hours?||?'nothing'}?hours?on?${this.project}`;
        ??}

        ??constructor(hours)?{
        ????this.#addHours?=?hours;
        ????console.log(this.#timeSheet);
        ??}
        }

        let?person?=?new?TimeTracker(4);?//?zhangsan?works?4?hours?on?blog

        由于嘗試訪問對象上不存在的私有字段會發(fā)生異常,因此需要能夠檢查對象是否具有給定的私有字段。可以使用 in 運算符來檢查對象上是否有私有字段:

        class?Example?{
        ??#field

        ??static?isExampleInstance(object)?{
        ????return?#field?in?object;
        ??}
        }

        查看更多公有和私有字段提案信息:https://github.com/tc39/proposal-class-fields

        3. 靜態(tài)公共字段

        在ES6中,不能在類的每個實例中訪問靜態(tài)字段或方法,只能在原型中訪問。ES 2022 將提供一種在 JavaScript 中使用 static 關(guān)鍵字聲明靜態(tài)類字段的方法。下面來看一個例子:

        class?Shape?{
        ??static?color?=?'blue';

        ??static?getColor()?{
        ????return?this.color;
        ??}

        ??getMessage()?{
        ????return?`color:${this.color}`?;
        ??}
        }

        我們可以從類本身訪問靜態(tài)字段和方法:

        ??console.log(Shape.color);?//?blue
        ??console.log(Shape.getColor());?//?blue
        ??console.log('color'?in?Shape);?//?true
        ??console.log('getColor'?in?Shape);?//?true
        ??console.log('getMessage'?in?Shape);?//?false

        實例不能訪問靜態(tài)字段和方法:

        ??const?shapeInstance?=?new?Shape();
        ??console.log(shapeInstance.color);?//?undefined
        ??console.log(shapeInstance.getColor);?//?undefined
        ??console.log(shapeInstance.getMessage());// color:undefined

        靜態(tài)字段只能通過靜態(tài)方法訪問:

        console.log(Shape.getColor());?//?blue
        console.log(Shape.getMessage());?//TypeError:?Shape.getMessage?is?not?a?function

        這里的 Shape.getMessage() 就報錯了,這是因為 getMessage 不是一個靜態(tài)函數(shù),所以它不能通過類名 Shape 訪問??梢酝ㄟ^以下方式來解決這個問題:

        getMessage()?{
        ??return?`color:${Shape.color}`?;
        }

        靜態(tài)字段和方法是從父類繼承的:

        class?Rectangle?extends?Shape?{?}

        console.log(Rectangle.color);?//?blue
        console.log(Rectangle.getColor());?//?blue
        console.log('color'?in?Rectangle);?//?true
        console.log('getColor'?in?Rectangle);?//?true
        console.log('getMessage'?in?Rectangle);?//?false

        4. 靜態(tài)私有字段和方法

        與私有實例字段和方法一樣,靜態(tài)私有字段和方法也使用哈希 (#) 前綴來定義:

        class?Shape?{
        ??static?#color?=?'blue';

        ??static?#getColor()?{
        ????return?this.#color;
        ??}

        ??getMessage()?{
        ????return?`color:${Shape.#getColor()}`?;
        ??}
        }
        const?shapeInstance?=?new?Shape();
        shapeInstance.getMessage();?// color:blue

        私有靜態(tài)字段有一個限制:只有定義私有靜態(tài)字段的類才能訪問該字段。這可能在我們使用 this 時導(dǎo)致出乎意料的情況:

        class?Shape?{
        ??static?#color?=?'blue';
        static?#getColor()?{
        ??return?this.#color;
        }
        static?getMessage()?{
        ??return?`color:${this.#color}`?;
        }
        getMessageNonStatic()?{
        ??return?`color:${this.#getColor()}`?;
        }
        }

        class?Rectangle?extends?Shape?{}

        console.log(Rectangle.getMessage());?//?Uncaught?TypeError:?Cannot?read?private?member?#color?from?an?object?whose?class?did?not?declare?it
        const?rectangle?=?new?Rectangle();
        console.log(rectangle.getMessageNonStatic());?//?TypeError:?Cannot?read?private?member?#getColor?from?an?object?whose?class?did?not?declare?it

        在這個例子中,this 指向的是 Rectangle 類,它無權(quán)訪問私有字段 #color。當我們嘗試調(diào)用 Rectangle.getMessage() 時,它無法讀取 #color 并拋出了 TypeError??梢赃@樣來進行修改:

        class?Shape?{
        ??static?#color?=?'blue';
        ??static?#getColor()?{
        ????return?this.#color;
        ??}
        ??static?getMessage()?{
        ????return?`${Shape.#color}`;
        ??}
        ??getMessageNonStatic()?{
        ????return?`color:${Shape.#getColor()}?color`;
        ??}
        }

        class?Rectangle?extends?Shape?{}
        console.log(Rectangle.getMessage());?// color:blue
        const?rectangle?=?new?Rectangle();
        console.log(rectangle.getMessageNonStatic());?// color:blue

        靜態(tài)字段目前是比較穩(wěn)定的,并且提供了各種實現(xiàn):

        5. 類靜態(tài)初始化塊

        靜態(tài)私有和公共字段只能讓我們在類定義期間執(zhí)行靜態(tài)成員的每個字段初始化。如果我們需要在初始化期間像 try … catch 一樣進行異常處理,就不得不在類之外編寫此邏輯。該提案就提供了一種在類聲明/定義期間評估靜態(tài)初始化代碼塊的優(yōu)雅方法,可以訪問類的私有字段。

        先來看一個例子:

        class?Person?{
        ????static?GENDER?=?"Male"
        ????static?TOTAL_EMPLOYED;
        ????static?TOTAL_UNEMPLOYED;

        ????try?{
        ????????//?...
        ????}?catch?{
        ????????//?...
        ????}
        }

        上面的代碼就會引發(fā)錯誤,可以使用類靜態(tài)塊來重構(gòu)它,只需將try...catch包裹在 static 中即可:

        class?Person?{
        ????static?GENDER?=?"Male"
        ????static?TOTAL_EMPLOYED;
        ????static?TOTAL_UNEMPLOYED;
        ????
        ??static?{
        ???try?{
        ????????//?...
        ????}?catch?{
        ????????//?...
        ????}
        ??}
        }

        此外,類靜態(tài)塊提供對詞法范圍的私有字段和方法的特權(quán)訪問。這里需要在具有實例私有字段的類和同一范圍內(nèi)的函數(shù)之間共享信息的情況下很有用。

        let?getData;

        class?Person?{
        ??#x
        ??
        ??constructor(x)?{
        ????this.#x?=?{?data:?x?};
        ??}

        ??static?{
        ????getData?=?(obj)?=>?obj.#x;
        ??}
        }

        function?readPrivateData(obj)?{
        ??return?getData(obj).data;
        }

        const?john?=?new?Person([2,4,6,8]);

        readPrivateData(john);?//?[2,4,6,8]

        這里,Person 類與 readPrivateData 函數(shù)共享了私有實例屬性。

        三、Temporal

        JavaScript 中的日期處理 Date() 對象一直是飽受詬病,該對象是1995 年受到 Java 的啟發(fā)而實現(xiàn)的,自此就一直沒有改變過。雖然Java已經(jīng)放棄了這個對象,但是 Date() 仍保留在 JavaScript 中來實現(xiàn)瀏覽器的兼容。

        Date() API 存在的問題:

        • 只支持UTC和用戶的PC時間;
        • 不支持公歷以外的日歷;
        • 字符串到日期解析容易出錯;
        • Date 對象是可變的,比如:
        const?today?=?new?Date();
        const?tomorrow?=?new?Date(today.setDate(today.getDate()?+?1));

        console.log(tomorrow);??
        console.log(today);?????

        此時,兩個時間輸出是一樣的,不符合我們的預(yù)期。正因為 Date() 對象存在的種種問題。平時我們經(jīng)常需要借助moment.jsDay.js等日期庫,但是它們的體積較大,有時一個簡單的日期處理就需要引入一個庫,得不償失。

        目前,由于Date API 在很多庫和瀏覽器引擎中的廣泛使用,沒有辦法修復(fù)API的不好的部分。而改變Date API 的工作方式也很可能會破壞許多網(wǎng)站和庫。

        正因如此,TC39提出了一個全新的用于處理日期和時間的標準對象和函數(shù)——Temporal。新的Temporal API 提案旨在解決Date API的問題。它為 JavaScript 日期/時間操作帶來了以下修復(fù):

        • 僅可以創(chuàng)建和處理不可變Temporal對象;
        • 提供用于日期和時間計算的簡單 API;
        • 支持所有時區(qū);
        • 從 ISO-8601 格式進行嚴格的日期解析;
        • 支持非公歷。

        Temporal 將取代 Moment.js 之類的庫,這些庫很好地填補了 JavaScript 中的空白,這種空白非常普遍,因此將功能作為語言的一部分更有意義。

        由于該提案還未正式發(fā)布,所以,可以借助官方提供的prlyfill來測試。首選進行安裝:

        npm?install?@js-temporal/polyfill

        導(dǎo)入并使用:

        import?{?Temporal?}?from?'@js-temporal/polyfill';

        console.log(Temporal);

        Temporal 對象如下:

        下面就來看看 Temporal 對象有哪些實用的功能。

        1. 當前時間和日期

        Temporal.Now 會返回一個表示當前日期和時間的對象:

        //?自1970年1月1日以來的時間(秒和毫秒)
        Temporal.Now.instant().epochSeconds;
        Temporal.Now.instant().epochMilliseconds;

        //?當前位置的時間
        Temporal.Now.zonedDateTimeISO();

        //?當前時區(qū)
        Temporal.Now.timeZone();

        //?指定時區(qū)的當前時間
        Temporal.Now.zonedDateTimeISO('Europe/London');

        2. 實例時間和日期

        Temporal.Instant 根據(jù) ISO 8601 格式的字符串返回一個表示日期和時間的對象,結(jié)果會精確到納秒:

        Temporal.Instant.from('2022-02-01T05:56:78.999999999+02:00[Europe/Berlin]');
        //?輸出結(jié)果:2022-02-01T03:57:18.999999999Z
        Temporal.Instant.from('2022-02-011T05:06+07:00');
        //?輸出結(jié)果:2022-01-31T22:06:00Z

        除此之外,我們還可以獲取紀元時間的對應(yīng)的日期(UTC 1970年1月1日0點是紀元時間):

        Temporal.Instant.fromEpochSeconds(1.0e8);
        //?輸出結(jié)果:1973-03-03T09:46:40Z

        3. 時區(qū)日期和時間

        Temporal.ZonedDateTime 返回一個對象,該對象表示在特定時區(qū)的日期/時間:

        new?Temporal.ZonedDateTime(
        ??1234567890000,?//?紀元時間
        ??Temporal.TimeZone.from('Europe/London'),?//?時區(qū)
        ??Temporal.Calendar.from('iso8601')?//?默認日歷
        );

        Temporal.ZonedDateTime.from('2025-09-05T02:55:00+02:00[Africa/Cairo]');

        Temporal.Instant('2022-08-05T20:06:13+05:45').toZonedDateTime('+05:45');
        //?輸出結(jié)果:

        Temporal.ZonedDateTime.from({
        ??timeZone:?'America/New_York',
        ??year:?2025,
        ??month:?2,
        ??day:?28,
        ??hour:?10,
        ??minute:?15,
        ??second:?0,
        ??millisecond:?0,
        ??microsecond:?0,
        ??nanosecond:?0
        });
        //?輸出結(jié)果:2025-02-28T10:15:00-05:00[America/New_York]?

        4. 簡單的日期和時間

        我們并不會總是需要使用精確的時間,因此 Temporal API 提供了獨立于時區(qū)的對象。這些可以用于更簡單的活動。

        • Temporal.PlainDateTime:指日歷日期和時間;
        • Temporal.PlainDate:指特定的日歷日期;
        • Temporal.PlainTime:指一天中的特定時間;
        • Temporal.PlainYearMonth:指沒有日期成分的日期,例如“2022 年 2 月”;
        • Temporal.PlainMonthDay:指沒有年份的日期,例如“10 月 1 日”。

        它們都有類似的構(gòu)造函數(shù),以下有兩種形式來創(chuàng)建簡單的時間和日期:

        new?Temporal.PlainDateTime(2021,?5,?4,?13,?14,?15);
        Temporal.PlainDateTime.from('2021-05-04T13:14:15');

        new?Temporal.PlainDate(2021,?5,?4);
        Temporal.PlainDate.from('2021-05-04');

        new?Temporal.PlainTime(13,?14,?15);
        Temporal.PlainTime.from('13:14:15');

        new?Temporal.PlainYearMonth(2021,?4);
        Temporal.PlainYearMonth.from('2019-04');

        new?Temporal.PlainMonthDay(3,?14);
        Temporal.PlainMonthDay.from('03-14');

        5. 日期和時間值

        所有 Temporal 對象都可以返回特定的日期/時間值。例如,使用ZonedDateTime:

        const?t1?=?Temporal.ZonedDateTime.from('2025-12-07T03:24:30+02:00[Africa/Cairo]');

        t1.year;????????//?2025
        t1.month;???????//?12
        t1.day;?????????//?7
        t1.hour;????????//?3
        t1.minute;??????//?24
        t1.second;??????//?30
        t1.millisecond;?//?0
        t1.microsecond;?//?0
        t1.nanosecond;??//?0

        其他有用的屬性包括:

        • dayOfWeek(周一為 1 至周日為 7)
        • dayOfYear(1 至 365 或 366)
        • weekOfYear(1 到 52,有時是 53)
        • daysInMonth(28、29、30、31)
        • daysInYear(365 或 366)
        • inLeapYear(true或false)

        6. 比較和排序日期

        所有 Temporal 對象都可以使用 compare() 返回整數(shù)的函數(shù)進行比較。例如,比較兩個ZonedDateTime對象:

        Temporal.ZonedDateTime.compare(t1,?t2);

        這個比較結(jié)果會有三種情況:

        • 當兩個時間值相等時,返回 0;
        • 當 t1 在 t2 之后時,返回 1;
        • 當 t1 在 t2 之前時,但會 -1;
        const?date1?=?Temporal.Now,
        const?date2?=?Temporal.PlainDateTime.from('2022-05-01');

        Temporal.ZonedDateTime.compare(date1,?date2);?//?-1

        compare() 的結(jié)果可以用于數(shù)組的 sort() 方法來對時間按照升序進行排列(從早到晚):

        const?t?=?[
        ????'2022-01-01T00:00:00+00:00[Europe/London]',
        ????'2022-01-01T00:00:00+00:00[Africa/Cairo]',
        ????'2022-01-01T00:00:00+00:00[America/New_York]'
        ].map(d?=>?Temporal.ZonedDateTime.from(d))
        ?.sort(Temporal.ZonedDateTime.compare);

        7. 日期計算

        提案還提供了幾種方法來對任何 Temporal 對象執(zhí)行日期計算。當傳遞一個Temporal.Duration對象時,它們都會返回一個相同類型的新的 Temporal,該對象使用years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds 和 nanoseconds 字段來設(shè)置時間。

        const?t1?=?Temporal.ZonedDateTime.from('2022-01-01T00:00:00+00:00[Europe/London]');

        t1.add({?hours:?8,?minutes:?30?});?//?往后8小時30分

        t1.subtract({?days:?5?});??//?往前5天

        t1.round({?smallestUnit:?'month'?});??//?四舍五入到最近的月份

        until() 和 since() 方法會返回一個對象,該 Temporal.Duration 對象描述基于當前日期/時間的特定日期和時間之前或之后的時間,例如:

        t1.until().months;?//?到t1還有幾個月

        t2.until().days;??//?到t2還有幾天

        t3.since().weeks;?//?t3已經(jīng)過去了幾周

        equals() 方法用來確定兩個日期/時間值是否相同:

        const?d1?=?Temporal.PlainDate.from('2022-01-31');
        const?d2?=?Temporal.PlainDate.from('2023-01-31');
        d1.equals(d2);??//?false

        8. 使用國際化 API 格式化日期

        雖然這不是 Temporal API 的一部分,但 JavaScript Intl(國際化)API提供了一個 DateTimeFormat() 構(gòu)造函數(shù),可以用于格式化 Temporal 或 Date 對象:

        const?d?=?new?Temporal.PlainDate(2022,?3,?14);

        //?美國日期格式:3/14/2022
        new?Intl.DateTimeFormat('en-US').format(d);

        //?英國日期格式:14/3/2022
        new?Intl.DateTimeFormat('en-GB').format(d);

        //?西班牙長日期格式:miércoles, 14 de abril de 2022
        new?Intl.DateTimeFormat('es-ES',?{?dateStyle:?'full'?}).format(d);

        附:

        • TC39 關(guān)于 Temporal 的提案進度:https://tc39.es/proposal-temporal/
        • Chrome 關(guān)于 Temporal 的實現(xiàn)進度:https://chromestatus.com/feature/5668291307634688#details

        四、內(nèi)置對象

        1. Object.hasOwn()

        在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 來檢查一個屬性是否屬于對象。

        提案中的 Object.hasOwn 特性是一種更簡潔、更可靠的檢查屬性是否直接設(shè)置在對象上的方法。

        const?example?=?{
        ??property:?'123'
        };

        console.log(Object.prototype.hasOwnProperty.call(example,?'property'));
        console.log(Object.hasOwn(example,?'property'));?

        2. at()

        at() 是一個數(shù)組方法,用于通過給定索引來獲取數(shù)組元素。當給定索引為正時,這種新方法與使用括號表示法訪問具有相同的行為。當給出負整數(shù)索引時,就會從數(shù)組的最后一項開始檢索:

        const?array?=?[0,1,2,3,4,5];

        console.log(array[array.length-1]);??//?5
        console.log(array.at(-1));??//?5

        console.log(array[array.lenght-2]);??//?4
        console.log(array.at(-2));??//?4

        除了數(shù)組,字符串也可以使用at()方法進行索引:

        const?str?=?"hello?world";

        console.log(str[str.length?-?1]);??//?d
        console.log(str.at(-1));??//?d

        3. cause

        在 ECMAScript 2022 提案中,new Error() 中可以指定導(dǎo)致它的原因:

        function?readFiles(filePaths)?{
        ??return?filePaths.map(
        ????(filePath)?=>?{
        ??????try?{
        ????????//?···
        ??????}?catch?(error)?{
        ????????throw?new?Error(
        ??????????`While?processing?${filePath}`,
        ??????????{cause:?error}
        ????????);
        ??????}
        ????});
        }

        4. 正則表達式匹配索引

        這個新提案已經(jīng)進入第 4 階段,它將允許我們利用 d 字符來表示我們想要匹配字符串的開始和結(jié)束索引。以前,我們只能在字符串匹配操作期間獲得一個包含提取的字符串和索引信息的數(shù)組。在某些情況下,這是不夠的。因此,在這個新提案中,如果設(shè)置標志 /d,將額外獲得一個帶有開始和結(jié)束索引的數(shù)組。

        const?matchObj?=?/(a+)(b+)/d.exec('aaaabb');

        console.log(matchObj[1])?//?'aaaa'
        console.log(matchObj[2])?//?'bb'

        由于 /d 標識的存在,matchObj還有一個屬性.indices,它用來記錄捕獲的每個編號組:

        console.log(matchObj.indices[1])??//?[0,?4]
        console.log(matchObj.indices[2])??//?[4,?6]

        我們還可以使用命名組:

        const?matchObj?=?/(?a+)(?b+)/d.exec('aaaabb');

        console.log(matchObj.groups.as);??//?'aaaa'
        console.log(matchObj.groups.bs);??//?'bb'

        這里給兩個字符匹配分別命名為as和bs,然后就可以通過groups來獲取到這兩個命名分別匹配到的字符串。

        它們的索引存儲在 matchObj.indices.groups 中:

        console.log(matchObj.indices.groups.as);??//?[0,?4]
        console.log(matchObj.indices.groups.bs);??//?[4,?6]

        匹配索引的一個重要用途就是指向語法錯誤所在位置的解析器。下面的代碼解決了一個相關(guān)問題:它指向引用內(nèi)容的開始和結(jié)束位置

        const?reQuoted?=?/“([^”]+)”/dgu;
        function?pointToQuotedText(str)?{
        ??const?startIndices?=?new?Set();
        ??const?endIndices?=?new?Set();
        ??for?(const?match?of?str.matchAll(reQuoted))?{
        ????const?[start,?end]?=?match.indices[1];
        ????startIndices.add(start);
        ????endIndices.add(end);
        ??}
        ??let?result?=?'';
        ??for?(let?index=0;?index?????if?(startIndices.has(index))?{
        ??????result?+=?'[';
        ????}?else?if?(endIndices.has(index+1))?{
        ??????result?+=?']';
        ????}?else?{
        ??????result?+=?'?';
        ????}
        ??}
        ??return?result;
        }

        console.log(pointToQuotedText('They?said?“hello”?and?“goodbye”.'));
        //?'???????????[???]???????[?????]??'
        瀏覽 60
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 人妻体内射精一区二区 | 久热免费在线视频 | 午夜欧美精品久久久久 | 亚洲无码播放 | 操美女操美女逼毛 |