せっかちな人のためのJavaScript(ES2022版)
この本のサポートをお願いします:購入する または 寄付する
(広告です。ブロックしないでください。)

20 文字列



20.1 チートシート:文字列

JavaScriptでは、文字列はプリミティブ値であり、不変です。つまり、文字列関連の操作は常に新しい文字列を生成し、既存の文字列を変更することはありません。

20.1.1 文字列の操作

文字列のリテラル

const str1 = 'Don\'t say "goodbye"'; // string literal
const str2 = "Don't say \"goodbye\""; // string literals
assert.equal(
  `As easy as ${123}!`, // template literal
  'As easy as 123!',
);

バックスラッシュは、以下のために使用されます。

`String.raw` タグ付きテンプレート(行A)内では、バックスラッシュは通常の文字として扱われます

assert.equal(
  String.raw`\ \n\t`, // (A)
  '\\ \\n\\t',
);

値を文字列に変換する

> String(undefined)
'undefined'
> String(null)
'null'
> String(123.45)
'123.45'
> String(true)
'true'

文字列の一部をコピーする

// There is no type for characters;
// reading characters produces strings:
const str3 = 'abc';
assert.equal(
  str3[2], 'c' // no negative indices allowed
);
assert.equal(
  str3.at(-1), 'c' // negative indices allowed
);

// Copying more than one character:
assert.equal(
  'abc'.slice(0, 2), 'ab'
);

文字列を連結する

assert.equal(
  'I bought ' + 3 + ' apples',
  'I bought 3 apples',
);

let str = '';
str += 'I bought ';
str += 3;
str += ' apples';
assert.equal(
  str, 'I bought 3 apples',
);

20.1.2 JavaScriptの文字 vs. コードポイント vs. 字素クラスター

**JavaScriptの文字**は16ビットのサイズです。文字列でインデックス付けされ、`.length` でカウントされるものです。

**コードポイント**は、Unicodeテキストの構成要素です。ほとんどは1つのJavaScript文字に収まりますが、一部は2つ(特に絵文字)を占めます。

assert.equal(
  'A'.length, 1
);
assert.equal(
  '🙂'.length, 2
);

**字素クラスター**(*ユーザーが認識する文字*)は、書かれた記号を表します。それぞれが1つ以上のコードポイントで構成されます。

これらの事実により、テキストをJavaScriptの文字に分割するべきではなく、字素に分割するべきです。テキストの処理方法の詳細については、§20.7「テキストの構成要素:コードポイント、JavaScriptの文字、字素クラスター」を参照してください。

20.1.3 文字列メソッド

この項では、文字列APIの概要を簡単に説明します。本章の最後には、より包括的なクイックリファレンスがあります。

部分文字列の検索

> 'abca'.includes('a')
true
> 'abca'.startsWith('ab')
true
> 'abca'.endsWith('ca')
true

> 'abca'.indexOf('a')
0
> 'abca'.lastIndexOf('a')
3

分割と結合

assert.deepEqual(
  'a, b,c'.split(/, ?/),
  ['a', 'b', 'c']
);
assert.equal(
  ['a', 'b', 'c'].join(', '),
  'a, b, c'
);

パディングとトリミング

> '7'.padStart(3, '0')
'007'
> 'yes'.padEnd(6, '!')
'yes!!!'

> '\t abc\n '.trim()
'abc'
> '\t abc\n '.trimStart()
'abc\n '
> '\t abc\n '.trimEnd()
'\t abc'

繰り返しと大文字小文字の変更

> '*'.repeat(5)
'*****'
> '= b2b ='.toUpperCase()
'= B2B ='
> 'ΑΒΓ'.toLowerCase()
'αβγ'

20.2 プレーン文字列リテラル

プレーン文字列リテラルは、シングルクォートまたはダブルクォートで区切られます。

const str1 = 'abc';
const str2 = "abc";
assert.equal(str1, str2);

シングルクォートは、ダブルクォートが推奨されるHTMLを記述しやすいので、より頻繁に使用されます。

次の章では、以下を提供する*テンプレートリテラル*について説明します。

20.2.1 エスケープ

バックスラッシュを使用すると、特殊文字を作成できます。

バックスラッシュを使用すると、文字列リテラルの区切り文字をそのリテラル内で使用することもできます。

assert.equal(
  'She said: "Let\'s go!"',
  "She said: \"Let's go!\"");

20.3 JavaScriptの文字へのアクセス

JavaScriptには、文字用の特別なデータ型はありません。文字は常に文字列として表されます。

const str = 'abc';

// Reading a JavaScript character at a given index
assert.equal(str[1], 'b');

// Counting the JavaScript characters in a string:
assert.equal(str.length, 3);

画面に表示される文字は、*字素クラスター*と呼ばれます。それらのほとんどは、単一のJavaScript文字で表されます。ただし、複数のJavaScript文字で表される字素クラスター(特に絵文字)もあります。

> '🙂'.length
2

その仕組みについては、§20.7「テキストの構成要素:コードポイント、JavaScriptの文字、字素クラスター」で説明しています。

20.4 `+` による文字列連結

少なくとも1つのオペランドが文字列の場合、プラス演算子(`+`)は文字列以外のものを文字列に変換し、結果を連結します。

assert.equal(3 + ' times ' + 4, '3 times 4');

代入演算子 `+=` は、文字列を少しずつ組み立てたい場合に便利です。

let str = ''; // must be `let`!
str += 'Say it';
str += ' one more';
str += ' time';

assert.equal(str, 'Say it one more time');

  **`+` による連結は効率的です**

ほとんどのJavaScriptエンジンは内部で最適化しているため、`+` を使用して文字列を組み立てるのは非常に効率的です。

  **演習:文字列の連結**

exercises/strings/concat_string_array_test.mjs

20.5 文字列への変換

値 `x` を文字列に変換する3つの方法があります。

推奨:分かりやすく安全な `String()` を使用してください。

assert.equal(String(undefined), 'undefined');
assert.equal(String(null), 'null');

assert.equal(String(false), 'false');
assert.equal(String(true), 'true');

assert.equal(String(123.45), '123.45');

ブール値の落とし穴:`String()` を介してブール値を文字列に変換した場合、一般的に `Boolean()` を介して元に戻すことはできません。

> String(false)
'false'
> Boolean('false')
true

`Boolean()` が `false` を返す唯一の文字列は、空の文字列です。

20.5.1 オブジェクトの文字列化

プレーンオブジェクトには、あまり役に立たないデフォルトの文字列表現があります。

> String({a: 1})
'[object Object]'

配列はより良い文字列表現を持っていますが、それでも多くの情報を隠しています。

> String(['a', 'b'])
'a,b'
> String(['a', ['b']])
'a,b'

> String([1, 2])
'1,2'
> String(['1', '2'])
'1,2'

> String([true])
'true'
> String(['true'])
'true'
> String(true)
'true'

関数を文字列化すると、ソースコードが返されます。

> String(function f() {return 4})
'function f() {return 4}'

20.5.2 オブジェクトの文字列化のカスタマイズ

メソッド `toString()` を実装することで、オブジェクトを文字列化する組み込みの方法をオーバーライドできます。

const obj = {
  toString() {
    return 'hello';
  }
};

assert.equal(String(obj), 'hello');

20.5.3 値を文字列化する別の方法

JSONデータ形式は、JavaScript値のテキスト表現です。したがって、`JSON.stringify()` を使用して値を文字列に変換することもできます。

> JSON.stringify({a: 1})
'{"a":1}'
> JSON.stringify(['a', ['b']])
'["a",["b"]]'

注意点は、JSONは `null`、ブール値、数値、文字列、配列、およびオブジェクト(常にオブジェクトリテラルによって作成されたかのように扱われます)のみをサポートすることです。

ヒント:3番目のパラメータを使用すると、複数行出力に切り替え、インデントの量を指定できます。例:

console.log(JSON.stringify({first: 'Jane', last: 'Doe'}, null, 2));

このステートメントは、次の出力を生成します。

{
  "first": "Jane",
  "last": "Doe"
}

20.6 文字列の比較

文字列は、次の演算子を使用して比較できます。

< <= > >=

考慮すべき重要な注意点が1つあります。これらの演算子は、JavaScriptの文字の数値に基づいて比較します。つまり、JavaScriptが文字列に使用する順序は、辞書や電話帳で使用される順序とは異なります。

> 'A' < 'B' // ok
true
> 'a' < 'B' // not ok
false
> 'ä' < 'b' // not ok
false

テキストの適切な比較は、本書の範囲外です。ECMAScript Internationalization API(`Intl`)でサポートされています。

20.7 テキストの構成要素:コードポイント、JavaScriptの文字、字素クラスター

§19「Unicode - 簡単な紹介」の簡単な要約

次のコードは、単一のコードポイントが1つまたは2つのJavaScript文字で構成されていることを示しています。後者は `.length` を介してカウントします。

// 3 code points, 3 JavaScript characters:
assert.equal('abc'.length, 3);

// 1 code point, 2 JavaScript characters:
assert.equal('🙂'.length, 2);

次の表は、これまで説明してきた概念をまとめたものです。

エンティティ サイズ エンコード方法
JavaScript文字(UTF-16コードユニット) 16ビット
Unicodeコードポイント 21ビット 1〜2コードユニット
Unicode字素クラスター 1つ以上のコードポイント

20.7.1 コードポイントの操作

コードポイントを操作するためのJavaScriptのツールを見ていきましょう。

*Unicodeコードポイントエスケープ*を使用すると、コードポイントを16進数(1〜5桁)で指定できます。1つまたは2つのJavaScript文字が生成されます。

> '\u{1F642}'
'🙂'

  **Unicodeエスケープシーケンス**

ECMAScript言語仕様では、*Unicodeコードポイントエスケープ*と*Unicodeコードユニットエスケープ*(後で説明します)は、*Unicodeエスケープシーケンス*と呼ばれます。

`String.fromCodePoint()` は、単一のコードポイントを1〜2個のJavaScript文字に変換します。

> String.fromCodePoint(0x1F642)
'🙂'

`.codePointAt()` は、1〜2個のJavaScript文字を単一のコードポイントに変換します。

> '🙂'.codePointAt(0).toString(16)
'1f642'

文字列を*反復処理*できます。これは、JavaScript文字ではなくコードポイントにアクセスします。反復処理については、本書の後半で説明します。反復処理の1つの方法は、`for-of` ループを使用することです。

const str = '🙂a';
assert.equal(str.length, 3);

for (const codePointChar of str) {
  console.log(codePointChar);
}

// Output:
// '🙂'
// 'a'

`Array.from()` も反復処理に基づいており、コードポイントにアクセスします。

> Array.from('🙂a')
[ '🙂', 'a' ]

そのため、コードポイントをカウントするための優れたツールになります。

> Array.from('🙂a').length
2
> '🙂a'.length
3

20.7.2 コードユニット(文字コード)の操作

文字列のインデックスと長さは、JavaScript文字(UTF-16コードユニットで表される)に基づいています。

コードユニットを16進数で指定するには、正確に4桁の16進数を含む*Unicodeコードユニットエスケープ*を使用できます。

> '\uD83D\uDE42'
'🙂'

また、`String.fromCharCode()` を使用できます。*文字コード*は、標準ライブラリでの*コードユニット*の名称です。

> String.fromCharCode(0xD83D) + String.fromCharCode(0xDE42)
'🙂'

文字の文字コードを取得するには、`.charCodeAt()` を使用します。

> '🙂'.charCodeAt(0).toString(16)
'd83d'

20.7.3 ASCIIエスケープ

文字のコードポイントが256未満の場合、正確に2桁の16進数を含む*ASCIIエスケープ*を使用して参照できます。

> 'He\x6C\x6Co'
'Hello'

(ASCIIエスケープの正式名称は*16進エスケープシーケンス*です。16進数を使用した最初のエスケープでした。)

20.7.4 注意点:字素クラスター

あらゆる人間の言語で書かれている可能性のあるテキストを扱う場合は、コードポイントの境界ではなく、字素クラスターの境界で分割するのが最善です。

TC39は、Unicodeセグメンテーション(字素クラスター境界、単語境界、文境界など)をサポートするためのECMAScript Internationalization APIの提案である`Intl.Segmenter`に取り組んでいます。

その提案が標準になるまでは、利用可能なライブラリの1つを使用できます(「JavaScript grapheme」でウェブ検索してください)。

20.8 クイックリファレンス:文字列

20.8.1 文字列への変換

14 は、さまざまな値が文字列に変換される方法を示しています。

表 14: 値を文字列に変換する。
x `String(x)`
undefined 'undefined'
null 'null'
boolean false 'false', true 'true'
number 例: 123 '123'
bigint 例: 123n '123'
string x (入力、変更なし)
symbol 例: Symbol('abc') 'Symbol(abc)'
object 例えば、toString() で設定可能

20.8.2 テキスト原子の数値

20.8.3 String.prototype: 検索とマッチング

(String.prototype は、文字列のメソッドが格納されている場所です。)

20.8.4 String.prototype: 抽出

20.8.5 String.prototype: 結合

20.8.6 String.prototype: 変換

20.8.7 出典

  **演習: 文字列メソッドの使用**

exercises/strings/remove_extension_test.mjs

  **クイズ**

クイズアプリ を参照してください。