即將到來的 ECMAScript 2022 新特性

大家好,我是若川。持續組織了5個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。

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

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

  • stage0(strawman),任何TC39的成員都可以提交。

  • stage1(proposal),進入此階段就意味著這一提案被認為是正式的了,需要對此提案的場景與API進行詳盡的描述。

  • stage2(draft),演進到這一階段的提案如果能最終進入到標準,那么在之后的階段都不會有太大的變化,因為理論上只接受增量修改。

  • state3(candidate),這一階段的提案只有在遇到了重大問題才會修改,規范文檔需要被全面的完成。

  • state4(finished),這一階段的提案將會被納入到ES每年發布的規范之中

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

一、Top-level Await

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

頂層 await 允許我們在 async 函數外面使用 await 關鍵字,目前提案正處于第 4 階段,并且已經在三個主要的瀏覽器的 JavaScript 引擎中實現,模塊系統會協調所有的異步 promise。

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

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

//?a.jsimport?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.jsimport?{users}?from?'./a.js';console.log('users:?',?users);console.log('usingAwait?module');

我們還可以立即調用頂層async函數(IIAFE):

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

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

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

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

另一個方法是導出一個 promise,讓導入模塊知道數據已經準備好了:

//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);?
});

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

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

//?a.jsconst?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');const?users?=?resp.json();export?{?users};//?usingAwait.jsimport?{users}?from?'./a.mjs';console.log(users);console.log('usingAwait?module');

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

  • 動態加載模塊

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');
}

目前,在這些地方已經支持 Top-level await:

  • V8 v8.9

  • Webpack 5.0.0

  • Babel

  • Chrome DevTools REPL

  • Node REPL

二、類的實例成員

1. 公共實例字段

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

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>);}
}

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

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>);
}

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

下面來看看關于公共實例字段的注意事項:

  • 公共實例字段存在于每個創建的類實例上。它們要么是在Object.defineProperty()中添加,要么是在基類中的構造時添加(構造函數主體執行之前執行),要么在子類的super()返回之后添加:

class?Incrementor?{count?=?0
}const?instance?=?new?Incrementor();
console.log(instance.count);?//?0
  • 未初始化的字段會自動設置為 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?進行設置
person.timeSheet;

可以看到,在類中沒有任何措施可以防止在不調用 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 設為私有,只需要給這些方法名稱前面加#即可:

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

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

class?Example?{#fieldstatic?isExampleInstance(object)?{return?#field?in?object;}
}

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

3. 靜態公共字段

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

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

我們可以從類本身訪問靜態字段和方法:

console.log(Shape.color);?//?blueconsole.log(Shape.getColor());?//?blueconsole.log('color'?in?Shape);?//?trueconsole.log('getColor'?in?Shape);?//?trueconsole.log('getMessage'?in?Shape);?//?false

實例不能訪問靜態字段和方法:

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

靜態字段只能通過靜態方法訪問:

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

這里的 Shape.getMessage() 就報錯了,這是因為 getMessage 不是一個靜態函數,所以它不能通過類名 Shape 訪問。可以通過以下方式來解決這個問題:

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

靜態字段和方法是從父類繼承的:

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. 靜態私有字段和方法

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

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

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

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 類,它無權訪問私有字段 #color。當我們嘗試調用 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

靜態字段目前是比較穩定的,并且提供了各種實現:

cc7b02484be834d782fbc656ef6ffca8.png

5. 類靜態初始化塊

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

先來看一個例子:

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

上面的代碼就會引發錯誤,可以使用類靜態塊來重構它,只需將try...catch包裹在 static 中即可:

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

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

let?getData;class?Person?{#xconstructor(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 函數共享了私有實例屬性。

三、Temporal

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

Date() API 存在的問題:

  • 只支持UTC和用戶的PC時間;

  • 不支持公歷以外的日歷;

  • 字符串到日期解析容易出錯;

  • Date 對象是可變的,比如:

const?today?=?new?Date();
const?tomorrow?=?new?Date(today.setDate(today.getDate()?+?1));console.log(tomorrow);??
console.log(today);

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

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

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

  • 僅可以創建和處理不可變Temporal對象;

  • 提供用于日期和時間計算的簡單 API;

  • 支持所有時區;

  • 從 ISO-8601 格式進行嚴格的日期解析;

  • 支持非公歷。

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

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

npm?install?@js-temporal/polyfill

導入并使用:

import?{?Temporal?}?from?'@js-temporal/polyfill';console.log(Temporal);

Temporal 對象如下:

debcd3bc889f2884b4c795acfc24d086.png

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

1. 當前時間和日期

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

//?自1970年1月1日以來的時間(秒和毫秒)
Temporal.Now.instant().epochSeconds;
Temporal.Now.instant().epochMilliseconds;//?當前位置的時間
Temporal.Now.zonedDateTimeISO();//?當前時區
Temporal.Now.timeZone();//?指定時區的當前時間
Temporal.Now.zonedDateTimeISO('Europe/London');

2. 實例時間和日期

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

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

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

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

3. 時區日期和時間

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

new?Temporal.ZonedDateTime(1234567890000,?//?紀元時間Temporal.TimeZone.from('Europe/London'),?//?時區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');
//?輸出結果: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
});
//?輸出結果:2025-02-28T10:15:00-05:00[America/New_York]

4. 簡單的日期和時間

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

  • Temporal.PlainDateTime:指日歷日期和時間;

  • Temporal.PlainDate:指特定的日歷日期;

  • Temporal.PlainTime:指一天中的特定時間;

  • Temporal.PlainYearMonth:指沒有日期成分的日期,例如“2022 年 2 月”;

  • Temporal.PlainMonthDay:指沒有年份的日期,例如“10 月 1 日”。

它們都有類似的構造函數,以下有兩種形式來創建簡單的時間和日期:

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() 返回整數的函數進行比較。例如,比較兩個ZonedDateTime對象:

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

這個比較結果會有三種情況:

  • 當兩個時間值相等時,返回 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() 的結果可以用于數組的 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 對象執行日期計算。當傳遞一個Temporal.Duration對象時,它們都會返回一個相同類型的新的 Temporal,該對象使用years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds 和 nanoseconds 字段來設置時間。

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已經過去了幾周

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() 構造函數,可以用于格式化 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 關于 Temporal 的提案進度:https://tc39.es/proposal-temporal/

  • Chrome 關于 Temporal 的實現進度:https://chromestatus.com/feature/5668291307634688#details

四、內置對象

1. Object.hasOwn()

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

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

const?example?=?{property:?'123'
};console.log(Object.prototype.hasOwnProperty.call(example,?'property'));
console.log(Object.hasOwn(example,?'property'));

2. at()

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

const?array?=?[0,1,2,3,4,5];console.log(array[array.length-1]);??//?5
console.log(array.at(-1));??//?5console.log(array[array.lenght-2]);??//?4
console.log(array.at(-2));??//?4

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

const?str?=?"hello?world";console.log(str[str.length?-?1]);??//?d
console.log(str.at(-1));??//?d

3. cause

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

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

4. 正則表達式匹配索引

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

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?=?/(?<as>a+)(?<bs>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]

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

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?<?str.length;?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”.'));
//?'???????????[???]???????[?????]??'

bd4415de51ec9443c63d316ca39049c3.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

fbfec20fef1477c1d681ea0b3ed06dd0.png

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。分享、收藏、點贊、在看我的文章就是對我最大的支持~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274958.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274958.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274958.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

代碼備忘錄

1.用指針&#xff0c;函數調用實現交換兩個變量值 #include<stdio.h> int exchange(int *a,int *b){ int temp; temp*a; *a*b; *btemp;return 0;} int main(){ int i9,j2; int *p1,*p2; p1&i; p2&j; exchange(p1,p2); printf("p1%d,p2%d\n",*p1,*p2);…

mysql實戰38 | 都說InnoDB好,那還要不要使用Memory引擎?

我在上一篇文章末尾留給你的問題是&#xff1a;兩個 group by 語句都用了 order by null&#xff0c;為什么使用內存臨時表得到的語句結果里&#xff0c;0 這個值在最后一行&#xff1b;而使用磁盤臨時表得到的結果里&#xff0c;0 這個值在第一行&#xff1f;今天我們就來看看…

設計類的五個原則_內容設計的5個原則

設計類的五個原則重點 (Top highlight)There are many heuristics and principles for creating good content. Some are created from a UX perspective, others from a content marketing point of view. They range from very long to very concise ones. I reviewed a larg…

Umi 4 RC 發布

大家好&#xff0c;我是若川。感謝大家一年以來的支持和陪伴。這一年疫情反復&#xff0c;年底應該有由于疫情不能回家的小伙伴。在這里先祝福大家&#xff0c;新年快樂。本打算今天不發文&#xff0c;但看到這篇覺得不錯&#xff0c;就發一下。大家好&#xff0c;Umi 4 經過幾…

讓你沉迷的五種設計

讓你沉迷的五種設計 好游戲總是能令人沉迷其中無法自拔&#xff0c;外媒cracked經過分析&#xff0c;發現有五種設計方法必不可少&#xff1b; 1.斯金納箱原理&#xff1a;這是行為心理學派在實驗室內研究動物學習能力的箱形實驗裝置&#xff0c;游戲開發也得益于此&#xff0c…

Java學習路線詳解

有很多的[Java請添加鏈接描述](http://www.hfxms.com.cn/java/)程序員&#xff0c;在初期學習時&#xff0c;通常會對如何學習而感到迷茫。[合肥學碼思請添加鏈接描述](http://www.hfxms.com.cn/)小編就為大家分析如何學好Java編程&#xff0c;相信能幫助那些正在處于迷茫狀態的…

figma下載_在Figma中將約束與布局網格一起使用

figma下載While doing research for the book “Designing in Figma”, I discovered a powerful way to lay out objects using a combination of Layout Grid and Constraints. The interface of Figma does not indicate a connection between the two, so it can be discov…

換一種方式表達

http://player.youku.com/player.php/sid/XMjY2MTE5NDU2/v.swf 轉載于:https://www.cnblogs.com/JCSU/archive/2012/03/17/2403324.html

新的一年,碎片化學習前端,我推薦這幾個公眾號~

大家好&#xff0c;我是若川。假期余額不足&#xff0c;無法充值。快樂的時光總是短暫的。馬上又開始一年的學習和“奮斗”。前端技術日新月異&#xff0c;發展迅速&#xff0c;作為一個與時俱進的前端工程師&#xff0c;需要不斷的學習。這里強烈推薦幾個前端開發工程師必備的…

Java單元測試之JUnit4詳解

2019獨角獸企業重金招聘Python工程師標準>>> Java單元測試之JUnit4詳解 與JUnit3不同&#xff0c;JUnit4通過注解的方式來識別測試方法。目前支持的主要注解有&#xff1a; BeforeClass 全局只會執行一次&#xff0c;而且是第一個運行Before 在測試方法運行之前運行…

我在黑暗中看到你眼中的月光_你好黑暗,我的老朋友

我在黑暗中看到你眼中的月光(Originally published on https://web.dev/prefers-color-scheme/.)(最初發布于https://web.dev/prefers-color-scheme/ 。) 介紹 (Introduction) &#x1f4da; I have done a lot of background research on the history and theory of dark mod…

ant 實現批量打包android應用

很多的應用中需要加上應用推廣的統計&#xff0c;如果一個一個的去生成不同渠道包的應用&#xff0c;效率低不說&#xff0c;還有可能不小心弄錯了分發渠道&#xff0c;使用ant可以批量生成應用。一、添加渠道包信息為了統計渠道信息&#xff0c;就不得不在程序的某個地方加入渠…

Element Plus 正式版發布啦!

大家好&#xff0c;我是若川。祝大家新年快樂&#xff0c;開工大吉。公眾號回復「紅包」可以領取源碼共讀紅包封面。持續組織了6個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時…

大型網站技術架構(一)大型網站架構演化

2019獨角獸企業重金招聘Python工程師標準>>> 看完了有一本書&#xff0c;就應該有所收獲&#xff0c;有所總結&#xff0c;最近把《大型網站技術架構》一書給看完了&#xff0c;給人的印象實在深刻&#xff0c;再加上之前也搞過書本上講的反向代理和負載均衡以及ses…

永不示弱_永不過時的網頁設計:今天和2000年的在線投資組合

永不示弱重點 (Top highlight)Philippe Starck, a renowned industrial designer, once said:著名的工業設計師Philippe Starck曾經說過&#xff1a; “A designer has a duty to create timeless design. To be timeless you have to think really far into the future, not …

如何使用 React 創建一個作品集網站

大家好&#xff0c;我是若川。持續組織了6個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。今天&#xff0c;你…

博弈論入門 HDU 1850

Problem Description 一年在外 父母時刻牽掛春節回家 你能做幾天好孩子嗎寒假里嘗試做做下面的事情吧陪媽媽逛一次菜場悄悄給爸爸買個小禮物主動地 強烈地 要求洗一次碗某一天早起 給爸媽用心地做回早餐如果愿意 你還可以和爸媽說咱們玩個小游戲吧 ACM課上學的呢&#xff5e;下…

新的一年,如何高效學習前端前沿知識~

今天強烈推薦一些互聯網行業內優質技術公眾號&#xff0c;互聯網人大部分都關注了&#xff0c;包括目前主流的公司技術團隊號&#xff0c;技術社區號&#xff0c;個人技術號&#xff0c;這些號行業深耕已久&#xff0c;會給你帶來事半功倍的效果。公眾號那么多&#xff0c;文章…

RabbitMQ學習總結(7)——Spring整合RabbitMQ實例

2019獨角獸企業重金招聘Python工程師標準>>> 1.RabbitMQ簡介 RabbitMQ是流行的開源消息隊列系統&#xff0c;用erlang語言開發。RabbitMQ是AMQP&#xff08;高級消息隊列協議&#xff09;的標準實現。 官網&#xff1a;http://www.rabbitmq.com/ 2.Spring集成Rabbi…

UIScrollView實現不全屏分頁的小技巧

瀏覽一系列圖片時&#xff0c;我們會經常使用UIScrollView來進行實現&#xff0c;因為其自帶的pagingEnabled屬性&#xff0c;可以非常方便的自動支持手勢左右輕掃切換切換頁面。pagingEnabled不過最近做的一個產品&#xff0c;設計的效果有所不同&#xff0c;希望每一頁圖片的…