初心者向けにやさしく、実例たっぷりで噛み砕いて説明します。
ポイント → 「length は 文字コード単位(UTF-16のコードユニット) を数える」こと、「見た目の文字数(ユーザーが見る“文字”)」とは一致しない場合がある、そして実務でどう扱えばいいか、まで丁寧に示します。
基本の話:length で取れるもの
string.length は その文字列を内部で表現している UTF-16 のコードユニットの個数 を返します。
普通の英数字や多くの日本語は「1文字 = 1コードユニット」なので直感どおりですが、絵文字などは例外があります。
console.log("Hello".length); // 5
console.log("東京".length); // 2
console.log("あ".length); // 1
JavaScript罠:サロゲートペア(絵文字など)
一部の文字(絵文字や一部の拡張漢字など)は UTF-16 で 2つのコードユニット に分かれて格納されます。length はそのまま「2」と数えます。
const s1 = "😊"; // にこちゃん絵文字
console.log(s1.length); // 2 ← 見た目は1文字なのに length は 2
JavaScriptつまり、length は**「見た目の文字数」ではなく内部単位数**を返します。
よくある必要性と対処法
- 「入力を何文字まで許可する(例:ユーザー名は20文字まで)」
→ 絵文字を含むユーザー名だとlengthでは不正確になる可能性あり。見た目の文字数で判定したい場合は別のカウントが必要。 - 「文字列を 1 文字ずつ処理したい」
→for (let ch of str)やスプレッド・配列化でコードポイント単位(サロゲートペアを1つにまとめて扱える)でループできます。
コードポイント単位で扱う(サロゲートに対応)
const s = "a😊b";
console.log(s.length); // 4('😊' が 2)
console.log([...s].length); // 3(スプレッドはコードポイント単位で分割)
console.log(Array.from(s).length);// 3(同様)
JavaScriptでも — 「真の見た目の文字数(グリフ数)」はさらに別
合字(例:a + 結合振幅記号)や ZWJ(ゼロ幅結合子)で結合された複合絵文字(👩👩👧👦 のような家族絵文字)は、コードポイント数やスプレッドの個数とも一致しない場合があります。
「ユーザーが見る“1つの文字”」=グラフェムクラスタ(grapheme cluster) と呼ばれる概念で数えたい場合は専用ツールが必要です。
近年の良い方法:Intl.Segmenter
ブラウザの新しい機能 Intl.Segmenter を使うと、グラフェム単位で分割できます(ブラウザの対応状況に注意)。
function countGraphemes(str) {
if (typeof Intl !== "undefined" && Intl.Segmenter) {
const seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
return [...seg.segment(str)].length;
}
// フォールバック:Array.from でコードポイント数(完璧ではない)
return Array.from(str).length;
}
console.log(countGraphemes("a")); // 1
console.log(countGraphemes("😊")); // 1
console.log(countGraphemes("👩👩👧👦")); // 1(Segmenter なら 1 と数える)
JavaScript注意:
Intl.Segmenterはかなり良いが、古いブラウザではサポートされていないことがあるので、フォールバック(Array.fromなど)を用意します。完全に正確にしたい場合はgrapheme-splitterのようなライブラリ を使う選択肢もあります。
例題
以下は実際にブラウザのコンソールで試せる例です。
const list = [
"hello",
"東京",
"😊",
"a😊b",
"e\u0301", // e + combining acute accent(é を合成)
"👩👩👧👦" // ZWJ でつながった家族絵文字
];
for (const s of list) {
console.log("文字列:", s);
console.log(" length:", s.length); // UTF-16 コードユニット数
console.log(" Array.from:", Array.from(s).length); // コードポイント単位(サロゲート対応)
console.log(" [...seg]:", (Intl.Segmenter ? [...(new Intl.Segmenter(undefined,{granularity:"grapheme"})).segment(s)].length : "Segmenter未対応"));
console.log("---");
}
JavaScript期待される観察(環境による):
"hello"はすべて同じ(5)"東京"は length 2、Array.from も 2"😊"は length 2 だけど Array.from は 1"a😊b"は length 4、Array.from は 3e\u0301(e + 合成アクセント)は length 2、Array.from は 2、Segmenter は 1(見た目は é)👩👩👧👦は length が複雑(複数コードユニット)、Array.from は複数、Segmenter は 1(家族絵文字1つ)
よくある実践的な戦略
- 単純表示・短い英数字のみ →
lengthで十分。 - 絵文字や多言語を含む入力で「見た目の文字数」をカウントしたい →
Intl.Segmenterを優先、未対応環境はArray.fromをフォールバック。 - 完全な互換性が必要(サーバーサイド含む) →
grapheme-splitterなどのライブラリを採用するのが安全。
サンプル関数:入力を「見た目の文字数」で判定して制限する
function visibleLength(str) {
if (typeof Intl !== "undefined" && Intl.Segmenter) {
const seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
return [...seg.segment(str)].length;
}
return Array.from(str).length;
}
function isValidUserName(name, maxVisibleChars = 20) {
return visibleLength(name) <= maxVisibleChars;
}
console.log(isValidUserName("Halu", 20)); // true
console.log(isValidUserName("😊😊😊😊😊", 4)); // false (5絵文字)
JavaScript練習問題
"a\u0301"(a+ combining acute accent)はlengthとvisibleLengthはそれぞれ何?"🇯🇵"(国旗絵文字)はlengthとArray.fromとvisibleLengthはそれぞれ何?const s = "👩❤️💋👩"(キスするカップルの絵文字)を試して、3つの方法で数えて違いを観察せよ。
(答えは下にあります。自分で実行してから見ると学習効果大です)
答(ヒント付き)
"a\u0301"length→ 2(a と combining mark)Array.from→ 2(code points)visibleLength(Segmenterがあるなら) → 1(見た目は é)
"🇯🇵"(国旗)length→ 4(国旗は2つの地域指示子で、それぞれがサロゲートペアだから合計4)Array.from→ 2(2つのコードポイント)visibleLength→ 1(見た目は1つの国旗)
👩❤️💋👩(ZWJで結合された複合絵文字)lengthは大きな数(内部で複数のコードユニット)Array.fromは複数(複数の code points)visibleLength(Segmenter) → 1(見た目は1つ)
まとめ(初心者向けの要点)
string.lengthは 内部のコードユニット数 を返す(直感通りの「文字数」ではないことがある)。- 絵文字や拡張文字を含む場合は
lengthが見た目と一致しない。 - サロゲートペア対応には
Array.fromやスプレッド[...str]が便利(コードポイント単位)。 - 「本当に見た目の1文字単位で数える」なら
Intl.Segmenter(または外部ライブラリ)を使うのが確実。

