ECMAScript仕様輪読会 #103
前回: ECMAScript仕様輪読会 #102
Cosenseの招待リンク: https://scrapbox.io/projects/esspec/invitations/85b96c9fa718ce5185e307196ed8fb53
connpass: https://esspec.connpass.com/
ES Spec Draft: https://tc39.es/ecma262/
multipage: https://tc39.es/ecma262/multipage/
X hashtag: #esspec
便利ツール
esspec-memo: https://tars0x9752.github.io/esspec-memo/esspec.html
Scrapbox Codeblock Generator: https://vzvu3k6k.github.io/scrapbox-codeblock/
TC39 Terminology: https://github.com/tc39/how-we-work/blob/main/terminology.md
esmeta playground: https://es-meta.github.io/playground/
時事ネタ
フロカン福岡
https://frontend-conf.fukuoka.jp/2026/news/2026-05-11-general_call-for-proposal?hl=ja
フロカン関西
https://2026.frontend-conf.osaka.jp/
JSConf
https://jsconf.jp/2026/ja
フロカン東京
https://note.com/fec_tokyo/n/nfa9aa6606721
自己紹介 (近況報告)
syumai syumai.icon
X: https://x.com/__syumai GitHub: https://github.com/syumai
TSを書いて暮らしてます
フロカン北海道で話してきました
https://speakerdeck.com/syumai/jsx-runtime-basics
iwatsurut
とくに、イベントもなく過ごしています。
igrep(山本悠滋)
https://github.com/igrep/
関数型まつり 2026 スタッフ募集 https://blog.fp-matsuri.org/entry/2026/05/31/131337
neovideが便利なので関連する設定をごちゃごちゃいじってハマるのが最近の寝不足要因
maru。(まる)
https://x.com/sbleru
https://github.com/sbleru
主にTSで仕事してます。React, Node。
青森でワーケーションしてましたが青森よかったです(青森市がやってる体験モニター)
同時編集JavaScript Playground
前半
https://jssync.syumai.workers.dev/rooms/esspec_a
後半
https://jssync.syumai.workers.dev/rooms/esspec_b
前回のあらすじ
https://syumai.github.io/esspec/summaries/summary-102.html
今回のメモ
英語
For the purposes of this abstract operation, a decimal digit is a code unit in the inclusive interval from 0x0030 (DIGIT ZERO) to 0x0039 (DIGIT NINE).
「For the purposes of...」は、仕様書などの技術文書で「本節の目的において」や「この操作において」のように、その定義の適用範囲を限定する際によく使われる表現
code:js
// ===== Code =====
// https://tc39.es/ecma262/multipage/text-processing.html#sec-getsubstitution
console.log("abcdeabcde".replace(/bc/g, "$`"));
console.log("abcdeabcde".replace(/bc/g, "$&"));
console.log("abcdeabcde".replace(/bc/g, "$'"));
// ===== Output =====
aadeaabcdeade
abcdeabcde
adeabcdedeadede
code:js
// ===== Code =====
// https://tc39.es/ecma262/multipage/text-processing.html#sec-getsubstitution
// 通常のパターン
console.log("abcdeabcde".replace(/b(.)(.)/g, "$2$1"));
console.log("abcdeabcde".replace(/b(.)(.)/g, "$02$01"));
// vi. 2桁かつ、キャプチャ数をindexが超える時
console.log("abcdeabcde".replace(/b(.)(.)/g, "$25"));
console.log("abcdeabcde".replace(/b(.)(.)/g, "$256"));
// ix. Else のパターン
console.log("abcdeabcde".replace(/b(.)(.)/g, "$3"));
console.log("abcdeabcde".replace(/b(.)(.)/g, "$0"));
try {
console.log("abcdeabcde".replace(/b(.)(.)/g, "$$$$$"));
} catch (e) {
console.error(e);
}
{
let src = "b";
src += "(.)".repeat(100);
const regexp = new RegExp(src);
const target = "abcdef".repeat(50);
let replacement = "";
for (let i = 0; i < 100; i++) {
replacement += [$${i + 1}];
}
console.log(
JSON.stringify({
src, target, replacement
},
null,
2),
);
console.log(target.replace(regexp, replacement));
}
// ===== Output =====
adceadce
adceadce
ad5ead5e
ad56ead56e
a$3ea$3e
a$0ea$0e
a$$$ea$$$e
{
"src": "b(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)",
"target": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef",
"replacement": "$1$2$3$4$5$6$7$8$9$10$11$12$13$14$15$16$17$18$19$20$21$22$23$24$25$26$27$28$29$30$31$32$33$34$35$36$37$38$39$40$41$42$43$44$45$46$47$48$49$50$51$52$53$54$55$56$57$58$59$60$61$62$63$64$65$66$67$68$69$70$71$72$73$74$75$76$77$78$79$80$81$82$83$84$85$86$87$88$89$90$91$92$93$94$95$96$97$98$99$100"
}
acdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef0abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef
code:js
// ===== Code =====
// https://tc39.es/ecma262/multipage/text-processing.html#sec-getsubstitution
// 通常のパターン
console.log("abcdeabcde".replace(/b(.)(.)/g, "$2$1"));
console.log("abcdeabcde".replace(/b(.)(.)/g, "$02$01"));
console.log("abcdeabcde".replace(/(?<name>b.)/g, "$<name>"));
console.log("abcdeabcde".replace(/(?<undefined>b.)(?<name>d.)/g, "$<undefined>$<name>"));
// 日本語識別子
console.log("abcdeabcde".replace(/(?<あいうえお>b.)/g, "$<あいうえお>"));
// 数字から始まる識別子
// SyntaxError: Invalid regular expression: /(?<0abc>b.)/g: Invalid capture group name
// console.log("abcdeabcde".replace(/(?<0abc>b.)/g, "$<0abc>"));
console.log("2026-06-09".replace(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/, "$<day>/$<month>/$<year>"))
// => "09/06/2026"
{
try {
eval(console.log("abcdeabcde".replace(/(?<0abc>b.)/g, "[$<0abc>]")););
} catch(err) {
console.error(err);
}
}
// ===== Output =====
adceadce
adceadce
abcdeabcde
abcdeabcde
abcdeabcde
09/06/2026
SyntaxError: Invalid regular expression: /(?<0abc>b.)/g: Invalid capture group name
code:js
// ===== Code =====
// https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.search
console.log("abcdeabcde".search(/bc/));
console.log("abcdeabcde".search(/bd/));
const mySearcher = {
Symbol.search(thisValue) {
console.log(JSON.stringify({ thisValue }, null, 2));
return "xyz";
}
}
console.log("abcdeabcde".search(mySearcher));
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp#sticky_%E3%83%95%E3%83%A9%E3%82%B0%E4%BB%98%E3%81%8D%E3%81%AE%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE%E3%81%AE%E4%BD%BF%E7%94%A8
{
const str = "#foo#";
const regex = /foo/y;
regex.lastIndex = 1;
console.log(regex.test(str)); // true
regex.lastIndex = 5;
console.log(regex.test(str)); // false (sticky フラグがあるので lastIndex から始める)
regex.lastIndex; // 0 (照合に失敗した後はリセット)
const regex1 = /bc/y;
regex1.lastIndex = 5;
console.log("abcdeabcde".search(regex1));
}
// ===== Output =====
1
-1
{
"thisValue": "abcdeabcde"
}
xyz
true
false
-1