【実務・中級編】 String.fromCharCode() と String.fromCodePoint() – JavaScript実践ガイド

なぜ今さら「文字コード」の話なのか?:`fromCharCode` と `fromCodePoint` の決定的な境界線

現場でコードを書いていて、「絵文字が化けた」「特定の文字だけ検索に引っかからない」というバグに遭遇したことはないだろうか。もし君がこれまでに一度でもそんな泥沼にハマったことがあるなら、今日のテーマは一生モノの武器になるはずだ。

JavaScriptにおける文字列操作の基本は `String` メソッドだが、静的メソッドである `String.fromCharCode()` と `String.fromCodePoint()` の違いを曖昧に理解しているエンジニアは意外と多い。今回は、ブラウザが裏側でどう文字を扱っているのか、その「深淵」まで踏み込んで解説しよう。

—

1. 歴史的遺物としての `String.fromCharCode()`

まず `String.fromCharCode()` だ。これはJavaScriptが生まれた当初から存在する古参メソッドで、UTF-16コードユニットを受け取って文字列を返す。

ここで重要なのは、UTF-16という規格の限界だ。歴史的に、基本的な文字は16ビット(2バイト)で収まっていたが、世界中のすべての文字を表現しようとすると16ビットでは足りなくなった。そこで登場したのが「サロゲートペア」という概念だ。

  • 何が起きるのか?:`fromCharCode` は16ビット単位でしか動かない。つまり、絵文字のような「補助面(Supplementary Planes)」にある文字を生成しようとすると、サロゲートペア(上位サロゲートと下位サロゲートの2つ)を自分で計算して渡さなければならない。

// 従来のやり方(サロゲートペアを意識する必要がある)
// 「😊」は UTF-16 では 0xD83D 0xDE0A のペアで構成される
const happy = String.fromCharCode(0xD83D, 0xDE0A);

console.log(happy); // “😊”
// これを「あ」のような感覚で1つの数値でやろうとすると?
console.log(String.fromCharCode(0x1F60A)); // “” ← 期待しない文字(ゴミ)が出る

見ての通り、非常に直感的ではない。コードを読んだ瞬間に「ああ、これは古い仕様に依存した書き方だな」とバレてしまうし、何よりバグの温床だ。

—

2. モダンな解法 `String.fromCodePoint()`

ES2015(ES6)で導入された `String.fromCodePoint()` は、まさにこの苦しみを解消するために生まれた。これはUnicodeコードポイント(文字の識別番号そのもの)を直接受け取る。

ブラウザのエンジン側は、渡された値が大きければ自動的にサロゲートペアを生成してくれる。開発者は「これはUTF-16の何番と何番か?」なんて小難しいことを考える必要は一切ない。

/

  • 実務での推奨:fromCodePoint を使おう
  • Unicodeのコードポイントをそのまま渡すだけで安全に文字列化できる

/
const emoji = String.fromCodePoint(0x1F60A);

console.log(emoji); // “😊”
console.log(emoji.length); // 2 (内部的にはサロゲートペアとして保持されている)

—

3. なぜ「中級者」こそここを突き詰めるべきか

「動けばいい」という段階を卒業した君なら、以下の2点に注意してほしい。

1. 文字列の長さ問題:
`’😊’.length` は `2` になる。これはサロゲートペアを2つの文字としてカウントしてしまうためだ。バリデーションで文字数制限をかける際は、`Array.from(str).length` やスプレッド演算子 `[…str].length` を使う癖をつけよう。これらはイテレータを通して正しく文字を数えてくれる。

2. 正規表現での罠:
古いブラウザや環境を考慮しないコードを書くと、絵文字のマッチングで `.`(ドット)がサロゲートペアの半分を無視してしまい、文字列が破壊されることがある。現代のフロントエンドなら `u` フラグ(Unicodeフラグ)を正規表現に付与するのが鉄則だ。

// 実践的な例:絵文字を含む文字列の安全な分割と判定
const text = “Hello😊World”;

// 1. 正しい文字数カウント
console.log([…text].length); // 12 (😊を正しく1文字として扱う)

// 2. Unicodeフラグを使った正規表現
const hasEmoji = /\u{1F60A}/u.test(text);
console.log(hasEmoji); // true

—

まとめ:現場で迷わないための判断基準

  • `String.fromCharCode()`:レガシーなシステムとの通信や、特殊なビット操作が必要な場合を除き、基本的には使用しない。
  • `String.fromCodePoint()`:文字列を生成する際のデフォルトの選択肢。可読性が高く、安全である。

現場でコードをレビューする際、もし `fromCharCode` がずらりと並んでいたら、「ここは `fromCodePoint` でスッキリさせませんか?」と提案してみてほしい。それは単なる好みの問題ではなく、エンジニアとして「モダンな仕様を理解し、不具合の可能性を未然に防ぐ」というプロの姿勢を示すことになるからだ。

JavaScriptの仕様は一見複雑だが、こうして「なぜそうなっているのか」という背景を理解すれば、もう怖いものはない。明日のコーディングから、ぜひこの知識を活かしてほしい。

コメント

タイトルとURLをコピーしました