JavaScript のデータ型(1)文字列型を初心者向けに分かりやすく解説|JavaScript入門講座(3)

JavaScript
この記事は約16分で読めます。

JavaScript のデータ型(1)文字列型を初心者向けに分かりやすく解説|JavaScript入門講座(3)のPodcast

下記のPodcastは、Geminiで作成しました。

文字列型の本質とコンピュータサイエンスにおける位置付け

JavaScriptの世界において、文字列(String)は単なるテキストの断片ではなく、情報を伝達するための最も基本的かつ強力なプリミティブデータ型の一つとして定義されています。プログラミングの初学者が最初に触れる「Hello World」というメッセージから、数百万行に及ぶ大規模なデータベースのクエリ、あるいは複雑なWebアプリケーションのユーザーインターフェースに至るまで、文字列はあらゆる場所で情報の表現を支えています 。技術的な観点から見れば、JavaScriptにおける文字列は「16ビット符号なし整数値」が順番に並んだシーケンスであり、内部的には世界共通の文字規格であるUnicode、特にUTF-16という形式でエンコードされています 。文字列は「プリミティブ型」に分類されますが、これは数値や真偽値と同様に、メモリ上での扱いが非常に軽量であることを意味しています。しかし、JavaScriptエンジンは文字列に対して高度な抽象化を提供しており、開発者はあたかもオブジェクトを操作するように、豊富なメソッド群を呼び出してテキストを加工することができます 。この「プリミティブでありながらメソッドを持つ」という柔軟性が、JavaScriptを習得しやすく、かつ強力な言語にしている要因の一つです。また、JavaScriptの文字列を理解する上で避けて通れないのが「不変性(Immutability)」という概念です。一度メモリ上に生成された文字列は、後からその一部を書き換えることができません 。例えば、ある文字列の一部を置換したり、別の文字列を結合したりする操作を行うと、元の文字列が変更されるのではなく、変更後の内容を持つ全く新しい文字列が背後で生成されます 。この設計はメモリの効率的な管理やスレッドセーフな実行環境(JavaScriptはシングルスレッドですが、内部的な最適化には寄与します)を実現するために非常に重要であり、開発者が予期せぬデータの書き換えによるバグを防ぐ助けとなっています 。

文字列の生成とレキシカル文法

JavaScriptで文字列を定義する方法は、言語の進化とともに洗練されてきました。現代のJavaScript開発においては、用途に応じて最適な記述法を選択することが求められます。

文字列リテラルの三位一体

JavaScriptのソースコード内で文字列を直接記述する「文字列リテラル」には、主に3つの形式が存在します 。

シングルクォート (') とダブルクォート ("):これらは最も基本的な記述法であり、機能的な差はありません 。開発者の好みやプロジェクトの命名規則によって選ばれます。例えば、文字列の中にダブルクォートを含めたい場合は外側をシングルクォートで囲む('He said "Hello"')といった使い分けが一般的です 。

テンプレートリテラル (`):ES2015(ES6)から導入されたこの記述法は、現代の開発において標準的な地位を確立しました 。バックティック文字を使用することで、従来の引用符では困難だった「複数行にわたる記述」や「文字列内への変数の埋め込み(文字列補間)」を極めて直感的に行うことができます 。${expression} という構文を用いることで、複雑な計算結果や変数の値を、加算演算子(+)を使わずに文字列へ統合できます 。

Stringコンストラクタ:new String("text") のようにコンストラクタを呼び出すことで「文字列オブジェクト」を生成することも可能ですが、これは実務上ほとんど推奨されません 。プリミティブな文字列とオブジェクト形式の文字列では、eval() 関数の挙動や、比較演算子(===)での評価結果が異なるため、予期せぬ混乱を招く可能性があります 。

エスケープシーケンスと特殊文字

プログラム内で直接入力できない特殊な文字(改行やタブ、あるいは引用符自体)を表現するために、JavaScriptはバックスラッシュ(\)を用いたエスケープシーケンスを提供しています 。

シーケンス意味
\n改行 (Line Feed)
\r復帰 (Carriage Return)
\t水平タブ
\'シングルクォート
\"ダブルクォート
\\バックスラッシュ自体
\uXXXXUnicode文字(16進数指定)

これらのエスケープシーケンスを正しく理解することは、特に正規表現やファイルパスの処理を行う際に不可欠な知識となります。一方で、最新の String.raw 静的メソッドを使用すれば、これらのエスケープ処理を無効化した「生の文字列」を取得することも可能です 。

Unicodeと内部表現のメカニズム

JavaScriptがどのように文字をコンピュータ内部で保持しているかを知ることは、複雑なテキスト処理や多言語対応において決定的な差を生みます。

UTF-16とコードユニット

JavaScriptの文字列は、最小単位として「16ビットのコードユニット」を使用します 。多くの一般的な文字(アルファベット、数字、ひらがな、一般的な漢字など)は、この16ビット1つ分で表現できる「基本多言語面(BMP)」に収まっています 。この範囲内の文字を扱う限り、直感的な文字数とプログラム上の長さは一致します。

サロゲートペアという壁

しかし、絵文字や一部の希少な漢字、歴史的な文字などは16ビット(65,536通り)では表現しきれません。そこでJavaScript(およびUTF-16)は、2つの16ビットコードユニットを組み合わせて1つの文字を表現する「サロゲートペア」という手法を採用しています 。この仕組みこそが、初心者が最初につまずく「文字数の怪」の原因です。例えば、一つの絵文字(😄)を定義した場合、人間には1文字に見えますが、JavaScriptの内部では2つのコードユニットとして記録されます 。このため、後に詳述する .length プロパティはこの絵文字に対して 2 を返します 。この挙動を理解していないと、入力文字数のバリデーションや、文字列の切り出し操作において、文字が真っ二つに破壊されてしまう「泣き別れ」現象が発生し、バグの原因となります 。

ウェルフォームドな文字列への配慮

最新のECMAScript(ES2024以降)では、こうした Unicodeの不備に対する安全策が強化されています 。具体的には、不適切なペアになっているサロゲート(孤立サロゲート)が含まれているかどうかをチェックする isWellFormed() メソッドや、それらを安全な置換文字()に変換する toWellFormed() メソッドが追加されました 。これにより、Webアプリケーションが多言語を扱う際の堅牢性が飛躍的に向上しています 。

文字列のプロパティとアクセス手法

文字列の内容を読み取り、特定の位置にある情報を取得するためのインターフェースは複数存在します。

lengthプロパティの真実

文字列が持つ唯一のプロパティである length は、その文字列に含まれる「16ビットコードユニットの数」を返します 。

文字列の例内容length の値
"A"アルファベット1
"あ"ひらがな1
"漢"常用漢字1
"😄"絵文字2 (サロゲートペア)
"👨‍👩‍👧‍👧"家族の絵文字11 (結合文字の組み合わせ)

実務において正確な「視覚上の文字数」をカウントする必要がある場合は、文字列のイテレータを利用して配列に展開する方法([...str].length)や、最新の Intl.Segmenter APIを用いることが推奨されます 。

特定の文字へのアクセス:at() メソッドの優位性

文字列内の特定の文字を取得する方法として、歴史的には以下の3つがあります 。

ブラケット記法 (str[index]): 配列のようにアクセスできますが、存在しないインデックスに対しては undefined を返します 。

charAt(index) メソッド: 古くからある方法で、存在しないインデックスに対しては空文字 ("") を返します 。

at(index) メソッド: 2022年に追加された最新の推奨メソッドです 。

特に at() メソッドは、負の整数を指定することで「末尾からの位置」を簡単に指定できるという強力な利点を持っています 。例えば、str.at(-1) と記述するだけで、文字列の最後の1文字を確実に取得できます 。これは従来の str[str.length - 1] という冗長な書き方を置き換える、非常に洗練された手段です 。

文字列の検索と検証の自動化

Web開発の現場では、ユーザーの入力に特定のワードが含まれているか、あるいは特定の形式で始まっているかをチェックする処理が頻繁に発生します。

真偽値による高速な検証

現代のJavaScript開発では、インデックスを返す古い手法よりも、真偽値を直接返す直感的なメソッドが多用されます。

includes(searchString): 文字列の中に指定したワードが含まれているかを true/false で返します 。

startsWith(searchString): 文字列が特定の文字で始まっているかを確認します 。URLのプロトコル(https://)チェックなどに便利です。

endsWith(searchString): 文字列が特定の文字で終わっているかを確認します 。ファイル拡張子の判定などに役立ちます。

位置の特定と詳細な検索

位置情報を必要とする場合には、indexOf() や lastIndexOf() が使用されます 。これらは最初(または最後)に見つかった位置のインデックスを返しますが、見つからなかった場合には -1 を返すという点に注意が必要です。条件分岐で if (str.indexOf("target")) と書いてしまうと、インデックスが 0(先頭で見つかった場合)のときに「見つからなかった」と誤判定されるバグが発生しやすいため、現代では includes() の使用が優先されます 。

文字列の抽出・分割・変換のベストプラティクス

文字列を切り出し、加工するメソッド群は、JavaScriptにおいて最も使用頻度が高いものの一つです。ここでは、特に混同されやすいメソッドの差異を整理します。

slice() と substring() の決定的な違い

どちらも「開始位置」と「終了位置」を指定して部分文字列を取り出すメソッドですが、実務では slice() の使用が強く推奨されます 。

特徴slice(start, end)substring(start, end)
負の引数末尾からの位置として扱う0として扱う
引数の順序start > end なら空文字を返す引数を自動で入れ替える
推奨度非常に高い(直感的)低い(互換性のために維持)

特に負の値を指定した際の slice() の挙動は、文字列の末尾を基準に処理を行う場合に非常に便利であり、コードの可読性を高めます 。なお、類似のメソッドに substr() がありますが、これは現在「非推奨(deprecated)」となっており、将来のバージョンで削除される可能性があるため、絶対に使用を避けるべきです 。

分割と結合の技術

split(separator) メソッドは、文字列を特定の区切り文字で分割し、配列に変換します 。CSVデータの処理や、パスのパースなどで威力を発揮します。逆に、配列を文字列に戻す場合は配列側のメソッドである join() を使用し、これらの相互変換をスムーズに行うことが中級者への第一歩となります。

余分な空白の除去

ユーザー入力フォームからのデータ取得において欠かせないのが trim() メソッドです 。文字列の前後にある意図しないスペースや改行を取り除き、データの正規化を行います。最新の仕様では、前方のみを除去する trimStart() や、後方のみの trimEnd() も完備されています 。

高度な置換と最新のパターンマッチング

文字列内の特定のパターンを別の文字に置き換える操作は、replace() メソッドの独壇場でしたが、そこには長年、開発者を悩ませる仕様がありました。

replaceAll() の登場とその背景

従来の replace() メソッドは、第一引数に文字列を渡した場合、最初に見つかった1箇所しか置換しないという特性がありました 。全ての箇所を置換するためには、正規表現のグローバルフラグ(g)を駆使する必要があり、これが初学者の大きな壁となっていました。2020年に導入された replaceAll() は、この問題を根本から解決します 。名前の通り、見つかった全ての箇所を一括で置換します。ただし、replaceAll() に正規表現を渡す場合は、必ずグローバルフラグ(g)を設定しなければならないという制約があり、これを怠ると TypeError が発生します 。これは、意図しない無限ループやパフォーマンスの低下を防ぐための安全装置として機能しています。

正規表現の v フラグと次世代のテキスト処理

ECMAScript 2024では、正規表現に v フラグが追加され、文字列処理の能力がさらに強化されました 。これにより、文字クラスの中での集合演算(Aという文字集合からBという文字集合を引く、など)が可能になり、特定のUnicode特性に基づいた高度なマッチングが、より簡潔かつ正確に記述できるようになりました 。

文字列操作におけるパフォーマンスとメモリ管理の深淵

大規模なアプリケーションを開発する際、文字列の「不変性」はパフォーマンス上の課題となることがあります。

結合演算子のコスト

数万回のループ内で文字列を + 演算子で結合し続けると、その都度新しい文字列オブジェクトが生成され、ガベージコレクション(不要なメモリの回収)の負荷が増大します 。このような極端なケースでは、一度配列に各断片を格納し、最後に一括で join("") する手法が、メモリ効率の観点から有利になる場合があります。

文字列のインターニング

一方で、JavaScriptエンジン(V8など)は内部的に「文字列インターニング」という最適化を行っています 。これは、同じ内容の文字列リテラルが複数存在する場合、それらをメモリ上の同じアドレスに集約し、比較処理の高速化やメモリの節約を図る仕組みです。開発者がこの挙動を直接制御することはできませんが、リテラルを適切に使用することで、エンジンの最適化の恩恵を最大限に受けることができます 。

結論:文字列型をマスターするための地図

JavaScriptの文字列型は、単なる文字の羅列という枠を超え、Unicodeの複雑な仕様を内包し、言語の進化とともに洗練され続けてきた高度なデータ構造です。初心者にとって重要なのは、まずテンプレートリテラルによる柔軟な記述を身につけ、次に slice() や at() といった現代的なメソッドを選択する審美眼を養うことです。さらに、length プロパティがコードユニット単位であること、サロゲートペアによって視覚的な文字数とプログラム上の長さが乖離しうることという「Unicodeの深淵」を意識できるようになれば、もはや初心者ではありません。ES2024/2025で導入される isWellFormed() や v フラグといった最新機能を適切に使いこなすことで、世界中のユーザーが利用する複雑な多言語テキストを、安全かつ効率的に処理できるプロフェッショナルな開発者への道が開かれます 。JavaScript入門講座の次のステップでは、これらの文字列をさらに柔軟に操るための「配列型」や「オブジェクト型」との連携、そして高度なテキスト解析を実現する「正規表現」の世界へと進んでいくことになります。本稿で学んだ基礎知識は、そのすべての高度な技術を支える揺るぎない土台となるはずです。

参考資料

1. String - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String

2. テンプレートリテラル - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals

3. String.prototype.replaceAll() - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll

4. String.prototype.slice() - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/slice

5. String.prototype.substring() - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/substring

6. String.prototype.at() - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/at

7. String: length - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/length

8. JS:slice、substring、substrの違いと使い分け、実務での推奨メソッド, https://qiita.com/ktdatascience/items/7e0d384e4e6daa675035

9. 【JavaScript】String.prototype.slice(),.substring(),.substr() の違い, https://qiita.com/nanasi-1/items/1b1f48e001043e44404a

10. JavaScriptの文字数は1文字とは限らない?サロゲートペアの注意点, https://en-ambi.com/itcontents/entry/2020/04/28/103000/

11. JavaScriptにおけるサロゲートペアと異体字セレクタの扱い, https://qiita.com/koheki/items/8775271216cb98cbe726

12. Unicodeサロゲートペアとコードポイントのデコード規則, https://mizunashi-mana.github.io/blog/posts/2020/11/unicode-surrogate-codepoint/

13. What's New in JavaScript: ECMAScript 2024, https://www.syncfusion.com/blogs/post/whats-new-javascript-ecmascript-2024

14. New JavaScript features - Exploring JS, https://exploringjs.com/js/book/ch_new-javascript-features.html

15. New features in ECMAScript 2025, https://blog.saeloun.com/2025/07/08/new-features-in-ecmascript-2025/

16. ECMAScript 2024: Unicode and Internationalization improvements, https://saigontechnology.com/blog/ecmascript/

17. ECMAScript® 2025 Language Specification, https://tc39.es/ecma262/2025/

18. JavaScript Lexical grammar - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Lexical_grammar

19. String.raw() - JavaScript - MDN Web Docs, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/raw

コメント

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