ECMAScript仕様輪読会 #101
前回: ECMAScript仕様輪読会 #100
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://note.com/fec_tokyo/n/nfa9aa6606721
フロカン福岡
https://frontend-conf.fukuoka.jp/2026/news/2026-05-11-general_call-for-proposal?hl=ja
自己紹介 (近況報告)
syumai syumai.icon
X: https://x.com/__syumai GitHub: https://github.com/syumai
TSを書いて暮らしてます
来週TSKaigiで話します(資料WIP…)
iwatsurut
とくに、イベントもなく過ごしています。
igrep(山本悠滋)
https://github.com/igrep/
GWは自作DanceDanceRevolutionコントローラーを作ってPC版DDRを始めた。思いのほか快適
https://rhythmisskey.games/notes/alxbl0cz6b
maru。(まる)
https://x.com/sbleru
https://github.com/sbleru
主にTSで仕事してます。React, Node。
実家に帰って田植えしてました
yebis0942
人狼知能大会 自然言語部門に参加しています
https://aiwolfdial.github.io/aiwolf-nlp/
Cloudflare AIで参戦してみた
llama 3.3 70Bで参加したら無料枠を使い切ってしまった
llama 3.1 7Bだと「私は人狼です」とか口走ってしまう
gemini-3.1-flash-liteに載せ変えるとAIがもっともらしい嘘をつくのが見れて怖くなれる
同時編集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-100.html
今回のメモ
yebis0942.icon ECMAScriptのindexOf, startsWithなどの第2引数の挙動が難しいという話
一方でPythonはstart, endをオプショナルで受け付けるという仕様で一貫していてよさそう
例: str.index(sub[, start[, end]])
https://docs.python.org/ja/3/library/stdtypes.html#str.endswith
Rubyでは範囲指定はできない
https://docs.ruby-lang.org/ja/latest/method/String/i/end_with=3f.html
yebis0942.icon "honour (honor)"とは
en.wiktyonary.org - honor
To conform to, abide by, act in accordance with (an agreement, treaty, promise, request, or the like).
total ordering
全順序
"canonically equivalent"とは
https://unicode.org/reports/tr15/
languageとlocaleの違いは?
locale = ユーザーの言語や地域、文化的な設定を表す識別子
https://zenn.dev/dress_code/articles/eec808b3ad3273
code:js
// ===== Code =====
{
// Å ANGSTROM SIGN vs.
// Å LATIN CAPITAL LETTER A + COMBINING RING ABOVE
console.log("\u212B".localeCompare("A\u030A"));
// Ω OHM SIGN vs.
// Ω GREEK CAPITAL LETTER OMEGA
console.log("\u2126".localeCompare("\u03A9"));
// ṩ LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE vs.
// ṩ LATIN SMALL LETTER S + COMBINING DOT ABOVE + COMBINING DOT BELOW
console.log("\u1E69".localeCompare("s\u0307\u0323"));
// ḍ̇ LATIN SMALL LETTER D WITH DOT ABOVE + COMBINING DOT BELOW vs.
// ḍ̇ LATIN SMALL LETTER D WITH DOT BELOW + COMBINING DOT ABOVE
console.log("\u1E0B\u0323".localeCompare("\u1E0D\u0307"));
// 가 HANGUL CHOSEONG KIYEOK + HANGUL JUNGSEONG A vs.
// 가 HANGUL SYLLABLE GA
console.log("\u1100\u1161".localeCompare("\uAC00"));
console.log("㌀".localeCompare("アパート"));
}
// ===== Output =====
0
0
0
0
0
-1
ECMA-402
https://tc39.es/ecma402/#sup-String.prototype.localeCompare
code:js
// ===== Code =====
{
try {
String.prototype.localeCompare.call(null)
// String.prototype.localeCompare.call(undefined)
} catch (err) {
console.error(err);
}
}
{
console.log("abc".localeCompare("aba"));
console.log("abc".localeCompare("abb"));
console.log("abc".localeCompare("abc"));
console.log("abc".localeCompare("abd"));
console.log("abc".localeCompare("abe"));
}
{
console.log("z".localeCompare("ä", "de"));
console.log("z".localeCompare("ä", "en"));
console.log("z".localeCompare("ä", "sv"));
}
{
const items = "réservé", "Premier", "Cliché", "communiqué", "café", "Adieu";
console.log(items.toSorted((a, b) => a.localeCompare(b, "fr", { ignorePunctuation: true })));
console.log(items.toSorted((a, b) => a.localeCompare(b, "fr")));
}
{
const a = 'ä';
const b = 'z';
// ドイツ語では ä は a のバリエーションなので z より前
console.log(a.localeCompare(b, 'de')); // 負の値 (ä < z)
// スウェーデン語でも ä はアルファベットの後半(z の後)に位置する
console.log(a.localeCompare(b, 'sv')); // 正の値 (ä > z)
}
// ===== Output =====
TypeError: String.prototype.localeCompare called on null or undefined
1
1
0
-1
-1
1
1
-1
Adieu,café,Cliché,communiqué,Premier,réservé
Adieu,café,Cliché,communiqué,Premier,réservé
-1
1
code:js
// ===== Code =====
{
const nums = "一", "四十五", "三", "100", "3", "百", "二";
console.log(nums.toSorted((a, b) => a.localeCompare(b, "ja", { numeric: true })));
console.log(nums.toSorted((a, b) => a.localeCompare(b, "ja", { numeric: false })));
}
// ===== Output =====
3,100,一,三,四十五,二,百
100,3,一,三,四十五,二,百
code:js
// ===== Code =====
{
console.log("㌀".normalize("NFD"));
console.log("㌀".normalize("NFKD"), "length", "㌀".normalize("NFKD").length);
console.log("㌀".normalize("NFC"));
console.log("㌀".normalize("NFKC"), "length", "㌀".normalize("NFKC").length);
console.log("---");
console.log("アパート".normalize("NFD"));
console.log("アパート".normalize("NFKD"));
console.log("アパート".normalize("NFC"));
console.log("アパート".normalize("NFKC"));
console.log("---");
console.log("アパート".normalize("NFD"));
console.log("アパート".normalize("NFKD"));
console.log("アパート".normalize("NFC"));
console.log("アパート".normalize("NFKC"));
console.log("---");
console.log(String.fromCodePoint("㌀".normalize("NFKD").codePointAt(2))); // 丸
console.log(String.fromCodePoint("㌀".normalize("NFKC").codePointAt(2))); // 伸ばす記号
}
// ===== Output =====
㌀
アパート length 5
㌀
アパート length 4
---
アパート
アパート
アパート
アパート
---
アパート
アパート
アパート
アパート
---
゚
ー