JavaScriptの文字列は、見た目の「1文字」の集まりとして扱える場面もありますが、内部的にはUTF-16コード単位の並びとして扱われます。
一言でいうと
JavaScriptの length やインデックスは、見た目の文字数ではなくUTF-16コード単位を数えます。
文字列の基本
JavaScriptでは、文字列は string 型です。
const text = "JavaScript";
console.log(text.length);
console.log(text[0]);
英数字だけを扱ううちは、length がそのまま文字数に見えます。しかし、日本語、絵文字、結合文字を扱うと、見た目の文字数とずれることがあります。
UTF-16コード単位とは
JavaScriptの文字列操作の多くは、UTF-16コード単位を基準にします。
| 見方 | 意味 |
|---|---|
| 見た目の文字 | 人間が1文字と感じる単位 |
| Unicodeコードポイント | 文字に割り当てられた番号 |
| UTF-16コード単位 | JavaScriptが内部で扱う16ビット単位 |
多くの日本語や英数字は1つのUTF-16コード単位で表せます。一方、絵文字などは2つのコード単位になることがあります。
lengthで起きるズレ
console.log("A".length); // 1
console.log("あ".length); // 1
console.log("😀".length); // 2
😀 は見た目では1文字ですが、JavaScriptの length では2になります。
文字数チェックで length だけを見ると、絵文字や一部の文字で想定とずれることがあります。
インデックスで取り出す時の注意
const icon = "😀";
console.log(icon[0]);
console.log(icon[1]);
このように取り出すと、絵文字が途中で分割されます。画面表示やバリデーションで「1文字ずつ処理する」時は注意が必要です。
for…ofを使うとどうなるか
for...of は、文字列をコードポイント単位で走査します。
for (const char of "A😀B") {
console.log(char);
}
この場合、A、😀、B のように扱いやすくなります。ただし、結合文字や複数コードポイントで見た目1文字になるケースまで完全に解決するわけではありません。
よく使う文字列操作との関係
| 操作 | 注意点 |
|---|---|
length | UTF-16コード単位の数 |
text[i] | コード単位単位で取り出す |
charCodeAt() | コード単位の数値を返す |
codePointAt() | コードポイントを返せる |
for...of | コードポイント単位で回せる |
よくある誤解
| 誤解 | 実際 |
|---|---|
length は必ず人間が見る文字数 | UTF-16コード単位の数です |
text[0] は必ず1文字 | サロゲートペアを分割することがあります |
| 日本語なら全部同じ扱い | 文字種や正規化で違いがあります |
| 絵文字も普通の1文字 | 内部では複数コード単位の場合があります |
まとめ
JavaScriptの文字列は、内部的にUTF-16コード単位の並びとして扱われます。英数字や多くの日本語では意識しなくても動きますが、絵文字、特殊文字、厳密な文字数チェックでは length、charCodeAt()、codePointAt() の違いを理解する必要があります。