サロゲートペアの正しい扱い方(絵文字対応)
1. 問題の本質:length や charAt() がズレる
JavaScript の String は UTF-16 単位で扱うため、サロゲートペアを「2文字」と数えることがあります。
const s = '💖'; // ハートの絵文字
console.log(s.length); // 👉 2
console.log(s.charAt(0)); // 上位サロゲート(文字化け)
console.log(s.charAt(1)); // 下位サロゲート(文字化け)
JavaScript見た目では「💖」は1文字ですが、内部的には 2単位(2ワード)に分かれています。
2. 正しい扱い方の基本戦略
サロゲートペアを意識せずに安全に扱うには「コードポイント」を使う。
つまり:
codePointAt()(Unicodeコードポイント単位)を使う!charCodeAt()(UTF-16単位)ではなく、
const s = '💖';
console.log(s.codePointAt(0).toString(16)); // 👉 '1f496'(💖のUnicodeコードポイント)
JavaScript同様に、文字列を作るときも:
console.log(String.fromCodePoint(0x1f496)); // 💖
JavaScript3. 絵文字を安全に1文字ずつ処理する方法
方法A:スプレッド構文(推奨)
const text = 'A💖B😊';
const chars = [...text]; // サロゲートペアを1文字として分割
console.log(chars); // 👉 [ 'A', '💖', 'B', '😊' ]
console.log(chars.length); // 👉 4(見た目通り)
JavaScript方法B:Array.from()(同様の効果)
Array.from('A💖B😊'); // 👉 [ 'A', '💖', 'B', '😊' ]
JavaScriptこれらは内部的に codePointAt() を利用しており、サロゲートペアを自動的に正しく扱います。
4. 絵文字対応の substring() / slice() の安全版
JavaScript 標準の slice() はサロゲートペアを途中で切ることがあります。
→ そこで、「見た目上の文字単位」で安全に切り出す関数を自作します。
function safeSlice(str, start, end) {
return [...str].slice(start, end).join('');
}
const s = 'A💖B😊C';
console.log(safeSlice(s, 1, 3)); // 💖B
JavaScript5. 文字単位で安全にループ処理する
for (const ch of '💖😊🍀') {
console.log(ch);
}
JavaScript→ for...of ループはサロゲートペアを自動的に1文字として処理してくれます。
(for (let i = 0; i < str.length; i++) は NG)
6. 正しい長さを取得する (length の代替)
const emojiText = 'A💖B😊C';
console.log([...emojiText].length); // 5(見た目通りの文字数)
JavaScriptまとめ:安全な文字列処理セット
| 処理したいこと | NGな方法 | 正しい方法 |
|---|---|---|
| 文字コードを取得 | charCodeAt() | codePointAt() |
| 文字を生成 | String.fromCharCode() | String.fromCodePoint() |
| 文字数を数える | .length | [...str].length |
| 1文字ずつ処理 | for (let i...) | for...of または Array.from() |
| 部分文字列取得 | slice() | safeSlice() 関数など |
応用:サロゲートペア検出関数(レビュー用)
function hasSurrogatePair(str) {
return /[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(str);
}
console.log(hasSurrogatePair('💖')); // true
console.log(hasSurrogatePair('A')); // false
JavaScript実務Tips
- 絵文字や多国語文字を扱うサービス(チャット・SNSなど)では必須の知識。
- DB保存やAPI通信時に文字化け・長さ制限バグが出る原因は、ほぼこのサロゲートペアの扱いにある。
- 「文字数上限(例:140文字)」を設定する時も、
[...str].lengthでカウントすべき。

