その「文字」は本当に1文字か? `codePointAt` と `charCodeAt` の深い闇と光
現場でバリバリとフロントエンドを書いていると、「文字列のバリデーションなんて楽勝だ」と思う瞬間があるよな。でもな、絵文字や特殊な記号、あるいは歴史的な遺産であるサロゲートペアが絡んだ途端、その自信は脆くも崩れ去る。
今日は、中級エンジニアなら絶対に押さえておくべき「文字コードの正体」と、`charCodeAt` から `codePointAt` への進化について、現場の泥臭い視点から解説するぜ。
—
なぜ `charCodeAt` だけでは不十分なのか
結論から言うと、`charCodeAt` は「UTF-16のコードユニット」を返すメソッドであり、「文字そのもの」を返しているわけではないからだ。
JavaScriptの歴史を振り返ると、ECMAScriptの初期設計では「すべての文字は16bit(2バイト)で収まる」と想定されていた。だが、現実は残酷だ。絵文字や古代文字など、16bitに収まらない文字(補助文字)が登場し、それらは「サロゲートペア」という仕組みを使って、2つの16bitコードユニットで1つの文字を表現することになった。
現場で遭遇する「バグの温床」
もしお前が「文字列の先頭の文字コードが何か」を判定する際、`charCodeAt(0)` を安直に使っているとしたら、それは地雷を踏んでいる可能性がある。
// 「𠮷」(つちよし:サロゲートペア文字)
const str = “𠮷”;
// charCodeAt(0) は最初の16bitしか見ないため、文字全体を特定できない
console.log(str.charCodeAt(0)); // 55362 (全体の正体ではない)
// 2つ目のコードユニットも必要になる
console.log(str.charCodeAt(1)); // 57271
このように、`charCodeAt` を使うと、1文字を判定するためにロジックが複雑化し、可読性もメンテナンス性も著しく低下する。
—
救世主 `codePointAt` の登場
ES2015(ES6)で導入された `codePointAt` は、この悲劇を解決するために生まれた。こいつは「サロゲートペアを考慮して、完全なコードポイント(Unicodeの論理的な値)」を返してくれる。
現場での使い分けの基準はシンプルだ。「文字単位でループしたり、正規化したりする必要があるなら、絶対に `codePointAt` を使え」。
実践的なサンプル:サロゲートペアを正しく扱う
以下のコードを見てくれ。これが現場で「文字をイテレートする」際の正しいアプローチだ。
/
- 現場で使える!サロゲートペアを考慮した文字列操作の例
/
const text = “A𠮷B”;
// 1. charCodeAt を使うと文字が分割されてしまう
console.log([…text].map(char => char.charCodeAt(0)));
// 結果: [65, 55362, 66] ※ 55362 は「𠮷」の半分だけ
// 2. codePointAt を使えば、文字の論理値を正しく取得できる
for (let i = 0; i < text.length; i++) {
const code = text.codePointAt(i);
// codePointAt はサロゲートペアなら自動的に2つ分を読み取ってくれる
// ただし、i はインデックスなので「次の文字」へ移動する際は注意が必要
console.log(`インデックス ${i} のコードポイント: ${code.toString(16)}`);
// 4桁(基本多言語面)を超える文字(𠮷など)の場合、インデックスをスキップする工夫が必要
if (code > 0xFFFF) i++;
}
—
プロとしてのアドバイス:使い分けの指針
現場レベルでの判断基準を整理しておくぜ。
1. `charCodeAt` を使ってもいい場面
- レガシーな環境との互換性:ブラウザのサポート状況が極端に古い場合(とはいえ、今どきは Babel や Polyfill があるから言い訳にはなりにくいが)。
- 特定のインデックスのコードユニットを単に確認したいだけの場合:暗号処理や低レイヤーなバイナリ操作など、意図的にUTF-16の構造を触る場合。
2. `codePointAt` を使うべき場面
- ユーザー入力のバリデーション:絵文字を含めた文字数制限や入力チェックを行う場合。
- 文字列の変換・正規化:Unicodeの仕様に準拠した正確な処理が必要な場合。
- モダンな開発すべて:現代のJavaScript開発において、サロゲートペアを無視していい理由はどこにもない。
—
まとめ:結局のところ「文字」とは何か
エンジニアとして覚えておいてほしいのは、「画面に見えている『文字』は、プログラム上の『1つのスロット』とは限らない」という事実だ。
`charCodeAt` はプログラムの都合に合わせた「メモリの断片」を見ているに過ぎない。一方で `codePointAt` は、Unicodeという国際的な規格に基づいた「意味のある文字」を捉えようとしている。
現場でバグを埋め込まないための鉄則は、「文字列を扱うときは、常にサロゲートペアの存在を疑え」だ。これさえ意識できていれば、お前が書くコードの信頼性はグッと上がる。
何かあったらまた聞いてくれ。現場の泥臭い苦労話なら、いつでも付き合うぜ。

コメント