JavaScriptでは、文字列はプリミティブ値であり、不変です。つまり、文字列関連の操作は常に新しい文字列を生成し、既存の文字列を変更することはありません。
文字列のリテラル
const str1 = 'Don\'t say "goodbye"'; // string literal
const str2 = "Don't say \"goodbye\""; // string literals
.equal(
assert`As easy as ${123}!`, // template literal
'As easy as 123!',
; )
バックスラッシュは、以下のために使用されます。
`String.raw` タグ付きテンプレート(行A)内では、バックスラッシュは通常の文字として扱われます
.equal(
assertString.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';
.equal(
assert2], 'c' // no negative indices allowed
str3[;
).equal(
assert.at(-1), 'c' // negative indices allowed
str3;
)
// Copying more than one character:
.equal(
assert'abc'.slice(0, 2), 'ab'
; )
文字列を連結する
.equal(
assert'I bought ' + 3 + ' apples',
'I bought 3 apples',
;
)
let str = '';
+= 'I bought ';
str += 3;
str += ' apples';
str .equal(
assert, 'I bought 3 apples',
str; )
**JavaScriptの文字**は16ビットのサイズです。文字列でインデックス付けされ、`.length` でカウントされるものです。
**コードポイント**は、Unicodeテキストの構成要素です。ほとんどは1つのJavaScript文字に収まりますが、一部は2つ(特に絵文字)を占めます。
.equal(
assert'A'.length, 1
;
).equal(
assert'🙂'.length, 2
; )
**字素クラスター**(*ユーザーが認識する文字*)は、書かれた記号を表します。それぞれが1つ以上のコードポイントで構成されます。
これらの事実により、テキストをJavaScriptの文字に分割するべきではなく、字素に分割するべきです。テキストの処理方法の詳細については、§20.7「テキストの構成要素:コードポイント、JavaScriptの文字、字素クラスター」を参照してください。
この項では、文字列APIの概要を簡単に説明します。本章の最後には、より包括的なクイックリファレンスがあります。
部分文字列の検索
> 'abca'.includes('a')true
> 'abca'.startsWith('ab')true
> 'abca'.endsWith('ca')true
> 'abca'.indexOf('a')0
> 'abca'.lastIndexOf('a')3
分割と結合
.deepEqual(
assert'a, b,c'.split(/, ?/),
'a', 'b', 'c']
[;
).equal(
assert'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()'αβγ'
プレーン文字列リテラルは、シングルクォートまたはダブルクォートで区切られます。
const str1 = 'abc';
const str2 = "abc";
.equal(str1, str2); assert
シングルクォートは、ダブルクォートが推奨されるHTMLを記述しやすいので、より頻繁に使用されます。
次の章では、以下を提供する*テンプレートリテラル*について説明します。
バックスラッシュを使用すると、特殊文字を作成できます。
バックスラッシュを使用すると、文字列リテラルの区切り文字をそのリテラル内で使用することもできます。
.equal(
assert'She said: "Let\'s go!"',
"She said: \"Let's go!\"");
JavaScriptには、文字用の特別なデータ型はありません。文字は常に文字列として表されます。
const str = 'abc';
// Reading a JavaScript character at a given index
.equal(str[1], 'b');
assert
// Counting the JavaScript characters in a string:
.equal(str.length, 3); assert
画面に表示される文字は、*字素クラスター*と呼ばれます。それらのほとんどは、単一のJavaScript文字で表されます。ただし、複数のJavaScript文字で表される字素クラスター(特に絵文字)もあります。
> '🙂'.length2
その仕組みについては、§20.7「テキストの構成要素:コードポイント、JavaScriptの文字、字素クラスター」で説明しています。
少なくとも1つのオペランドが文字列の場合、プラス演算子(`+`)は文字列以外のものを文字列に変換し、結果を連結します。
.equal(3 + ' times ' + 4, '3 times 4'); assert
代入演算子 `+=` は、文字列を少しずつ組み立てたい場合に便利です。
let str = ''; // must be `let`!
+= 'Say it';
str += ' one more';
str += ' time';
str
.equal(str, 'Say it one more time'); assert
**`+` による連結は効率的です**
ほとんどのJavaScriptエンジンは内部で最適化しているため、`+` を使用して文字列を組み立てるのは非常に効率的です。
**演習:文字列の連結**
exercises/strings/concat_string_array_test.mjs
値 `x` を文字列に変換する3つの方法があります。
`String(x)`
`''+x`
推奨:分かりやすく安全な `String()` を使用してください。
例
.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'); assert
ブール値の落とし穴:`String()` を介してブール値を文字列に変換した場合、一般的に `Boolean()` を介して元に戻すことはできません。
> String(false)'false'
> Boolean('false')true
`Boolean()` が `false` を返す唯一の文字列は、空の文字列です。
プレーンオブジェクトには、あまり役に立たないデフォルトの文字列表現があります。
> 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}'
メソッド `toString()` を実装することで、オブジェクトを文字列化する組み込みの方法をオーバーライドできます。
const obj = {
toString() {
return 'hello';
};
}
.equal(String(obj), 'hello'); assert
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"
}
文字列は、次の演算子を使用して比較できます。
< <= > >=
考慮すべき重要な注意点が1つあります。これらの演算子は、JavaScriptの文字の数値に基づいて比較します。つまり、JavaScriptが文字列に使用する順序は、辞書や電話帳で使用される順序とは異なります。
> 'A' < 'B' // oktrue
> 'a' < 'B' // not okfalse
> 'ä' < 'b' // not okfalse
テキストの適切な比較は、本書の範囲外です。ECMAScript Internationalization API(`Intl`)でサポートされています。
§19「Unicode - 簡単な紹介」の簡単な要約
*コードポイント*は、Unicodeテキストの構成要素です。各コードポイントは21ビットのサイズです。
JavaScriptの文字列は、エンコーディング形式UTF-16を介してUnicodeを実装します。1つのコードポイントをエンコードするために、1つまたは2つの16ビットの*コードユニット*を使用します。
*字素クラスター*(*ユーザーが認識する文字*)は、画面または紙に表示される書かれた記号を表します。1つの字素クラスターをエンコードするには、1つ以上のコードポイントが必要です。
次のコードは、単一のコードポイントが1つまたは2つのJavaScript文字で構成されていることを示しています。後者は `.length` を介してカウントします。
// 3 code points, 3 JavaScript characters:
.equal('abc'.length, 3);
assert
// 1 code point, 2 JavaScript characters:
.equal('🙂'.length, 2); assert
次の表は、これまで説明してきた概念をまとめたものです。
エンティティ | サイズ | エンコード方法 |
---|---|---|
JavaScript文字(UTF-16コードユニット) | 16ビット | – |
Unicodeコードポイント | 21ビット | 1〜2コードユニット |
Unicode字素クラスター | 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';
.equal(str.length, 3);
assert
for (const codePointChar of str) {
console.log(codePointChar);
}
// Output:
// '🙂'
// 'a'
`Array.from()` も反復処理に基づいており、コードポイントにアクセスします。
> Array.from('🙂a')[ '🙂', 'a' ]
そのため、コードポイントをカウントするための優れたツールになります。
> Array.from('🙂a').length2
> '🙂a'.length3
文字列のインデックスと長さは、JavaScript文字(UTF-16コードユニットで表される)に基づいています。
コードユニットを16進数で指定するには、正確に4桁の16進数を含む*Unicodeコードユニットエスケープ*を使用できます。
> '\uD83D\uDE42''🙂'
また、`String.fromCharCode()` を使用できます。*文字コード*は、標準ライブラリでの*コードユニット*の名称です。
> String.fromCharCode(0xD83D) + String.fromCharCode(0xDE42)'🙂'
文字の文字コードを取得するには、`.charCodeAt()` を使用します。
> '🙂'.charCodeAt(0).toString(16)'d83d'
文字のコードポイントが256未満の場合、正確に2桁の16進数を含む*ASCIIエスケープ*を使用して参照できます。
> 'He\x6C\x6Co''Hello'
(ASCIIエスケープの正式名称は*16進エスケープシーケンス*です。16進数を使用した最初のエスケープでした。)
あらゆる人間の言語で書かれている可能性のあるテキストを扱う場合は、コードポイントの境界ではなく、字素クラスターの境界で分割するのが最善です。
TC39は、Unicodeセグメンテーション(字素クラスター境界、単語境界、文境界など)をサポートするためのECMAScript Internationalization APIの提案である`Intl.Segmenter`に取り組んでいます。
その提案が標準になるまでは、利用可能なライブラリの1つを使用できます(「JavaScript grapheme」でウェブ検索してください)。
表 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() で設定可能 |
String.fromCharCode()
[ES1].charCodeAt()
[ES1]String.fromCodePoint()
[ES6].codePointAt()
[ES6]String.prototype
: 検索とマッチング(String.prototype
は、文字列のメソッドが格納されている場所です。)
.endsWith(searchString: string, endPos=this.length): boolean
[ES6]
文字列の長さが endPos
であった場合、文字列が searchString
で終わる場合は true
を返します。それ以外の場合は false
を返します。
> 'foo.txt'.endsWith('.txt')true
> 'abcde'.endsWith('cd', 4)true
.includes(searchString: string, startPos=0): boolean
[ES6]
文字列に searchString
が含まれている場合は true
を、それ以外の場合は false
を返します。検索は startPos
から開始されます。
> 'abc'.includes('b')true
> 'abc'.includes('b', 2)false
.indexOf(searchString: string, minIndex=0): number
[ES1]
searchString
が文字列内に出現する最も低いインデックス、または出現しない場合は -1
を返します。返されるインデックスは minIndex
以上になります。
> 'abab'.indexOf('a')0
> 'abab'.indexOf('a', 1)2
> 'abab'.indexOf('c')-1
.lastIndexOf(searchString: string, maxIndex=Infinity): number
[ES1]
searchString
が文字列内に出現する最も高いインデックス、または出現しない場合は -1
を返します。返されるインデックスは maxIndex
以下になります。
> 'abab'.lastIndexOf('ab', 2)2
> 'abab'.lastIndexOf('ab', 1)0
> 'abab'.lastIndexOf('ab')2
[1 of 2] .match(regExp: string | RegExp): RegExpMatchArray | null
[ES3]
regExp
がフラグ /g
が設定されていない正規表現の場合、.match()
は文字列内で regExp
に最初に一致するものを返します。一致するものがない場合は null
を返します。 regExp
が文字列の場合、前述の手順を実行する前に、正規表現を作成するために使用されます (new RegExp()
のパラメータと考えてください)。
結果は次の型を持ちます
interface RegExpMatchArray extends Array<string> {
: number;
index: string;
input: undefined | {
groups: string]: string
[key;
} }
番号付きキャプチャグループは配列のインデックスになります(この型が Array
を拡張しているのはこのためです)。名前付きキャプチャグループ (ES2018) は .groups
のプロパティになります。このモードでは、.match()
は RegExp.prototype.exec()
のように動作します。
例
> 'ababb'.match(/a(b+)/){ 0: 'ab', 1: 'b', index: 0, input: 'ababb', groups: undefined }
> 'ababb'.match(/a(?<foo>b+)/){ 0: 'ab', 1: 'b', index: 0, input: 'ababb', groups: { foo: 'b' } }
> 'abab'.match(/x/)null
[2 of 2] .match(regExp: RegExp): string[] | null
[ES3]
regExp
のフラグ /g
が設定されている場合、.match()
はすべての一致を含む配列、または一致がない場合は null
を返します。
> 'ababb'.match(/a(b+)/g)[ 'ab', 'abb' ]
> 'ababb'.match(/a(?<foo>b+)/g)[ 'ab', 'abb' ]
> 'abab'.match(/x/g)null
.search(regExp: string | RegExp): number
[ES3]
文字列内で regExp
が出現するインデックスを返します。 regExp
が文字列の場合、正規表現を作成するために使用されます (new RegExp()
のパラメータと考えてください)。
> 'a2b'.search(/[0-9]/)1
> 'a2b'.search('[0-9]')1
.startsWith(searchString: string, startPos=0): boolean
[ES6]
searchString
が文字列のインデックス startPos
に出現する場合は true
を返します。それ以外の場合は false
を返します。
> '.gitignore'.startsWith('.')true
> 'abcde'.startsWith('bc', 1)true
String.prototype
: 抽出.slice(start=0, end=this.length): string
[ES3]
インデックス start
から始まり(start
を含む)、インデックス end
で終わる(end
を含まない)文字列の部分文字列を返します。インデックスが負の場合、使用される前に .length
に加算されます(-1
は this.length-1
になりますなど)。
> 'abc'.slice(1, 3)'bc'
> 'abc'.slice(1)'bc'
> 'abc'.slice(-2)'bc'
.at(index: number): string | undefined
[ES2022]
index
にある JavaScript の文字を文字列として返します。インデックスが負の場合、使用される前に `.length` に加算されます(`-1` は `this.length-1` になりますなど)。
> 'abc'.at(0)'a'
> 'abc'.at(-1)'c'
.split(separator: string | RegExp, limit?: number): string[]
[ES3]
文字列を部分文字列の配列に分割します。部分文字列は、セパレータの間に出現する文字列です。セパレータは文字列です
> 'a | b | c'.split('|')[ 'a ', ' b ', ' c' ]
正規表現にすることもできます
> 'a : b : c'.split(/ *: */)[ 'a', 'b', 'c' ]
> 'a : b : c'.split(/( *):( *)/)[ 'a', ' ', ' ', 'b', ' ', ' ', 'c' ]
最後の呼び出しは、正規表現のグループによって行われたキャプチャが、返された配列の要素になることを示しています.
**警告: .split('')
は文字列を JavaScript の文字に分割します。**これは、アストラルコードポイント (2 つの JavaScript 文字としてエンコードされる) を扱う場合はうまく機能しません。たとえば、絵文字はアストラルです
> '🙂X🙂'.split('')[ '\uD83D', '\uDE42', 'X', '\uD83D', '\uDE42' ]
代わりに、Array.from()
(またはスプレッド構文) を使用することをお勧めします
> Array.from('🙂X🙂')[ '🙂', 'X', '🙂' ]
.substring(start: number, end=this.length): string
[ES1]
このメソッドの代わりに .slice()
を使用してください。.substring()
は古いエンジンでは一貫して実装されておらず、負のインデックスをサポートしていません。
String.prototype
: 結合.concat(...strings: string[]): string
[ES3]
文字列と strings
の連結を返します。 'a'.concat('b')
は 'a'+'b'
と同等です。後者の方がはるかに一般的です。
> 'ab'.concat('cd', 'ef', 'gh')'abcdefgh'
.padEnd(len: number, fillString=' '): string
[ES2017]
文字列が目的の長さ len
になるまで、fillString
(の断片) を文字列の末尾に追加します。すでに len
と同じか、それを超えている場合は、変更せずに返されます。
> '#'.padEnd(2)'# '
> 'abc'.padEnd(2)'abc'
> '#'.padEnd(5, 'abc')'#abca'
.padStart(len: number, fillString=' '): string
[ES2017]
文字列が目的の長さ len
になるまで、fillString
(の断片) を文字列の先頭に追加します。すでに len
と同じか、それを超えている場合は、変更せずに返されます。
> '#'.padStart(2)' #'
> 'abc'.padStart(2)'abc'
> '#'.padStart(5, 'abc')'abca#'
.repeat(count=0): string
[ES6]
文字列を count
回連結したものを返します。
> '*'.repeat()''
> '*'.repeat(3)'***'
String.prototype
: 変換.normalize(form: 'NFC'|'NFD'|'NFKC'|'NFKD' = 'NFC'): string
[ES6]
Unicode 正規化形式 に従って文字列を正規化します。
[1 of 2] .replaceAll(searchValue: string | RegExp, replaceValue: string): string
[ES2021]
**
.replaceAll()
を使用できない場合の対処法**
対象プラットフォームで .replaceAll()
が使用できない場合は、代わりに .replace()
を使用できます。方法は §43.6.8.1 “str.replace(searchValue, replacementValue)
[ES3]” で説明されています.
searchValue
のすべての一致を replaceValue
で置き換えます。 searchValue
がフラグ /g
のない正規表現の場合、TypeError
がスローされます。
> 'x.x.'.replaceAll('.', '#')'x#x#'
> 'x.x.'.replaceAll(/./g, '#')'####'
> 'x.x.'.replaceAll(/./, '#')TypeError: String.prototype.replaceAll called with
a non-global RegExp argument
replaceValue
の特殊文字は次のとおりです。
$$
: $
になります$n
: 番号付きグループ n
のキャプチャになります (残念ながら、$0
は文字列 '$0'
を表し、完全一致は参照しません)$&
: 完全一致になります$`
: 一致する前のすべてになります$'
: 一致する後のすべてになります例
> 'a 1995-12 b'.replaceAll(/([0-9]{4})-([0-9]{2})/g, '|$2|')'a |12| b'
> 'a 1995-12 b'.replaceAll(/([0-9]{4})-([0-9]{2})/g, '|$&|')'a |1995-12| b'
> 'a 1995-12 b'.replaceAll(/([0-9]{4})-([0-9]{2})/g, '|$`|')'a |a | b'
名前付きキャプチャグループ (ES2018) もサポートされています
$<name>
は名前付きグループ name
のキャプチャになります例
.equal(
assert'a 1995-12 b'.replaceAll(
/(?<year>[0-9]{4})-(?<month>[0-9]{2})/g, '|$<month>|'),
'a |12| b');
[2 of 2] .replaceAll(searchValue: string | RegExp, replacer: (...args: any[]) => string): string
[ES2021]
2 番目のパラメータが関数の場合、出現箇所はそれが返す文字列に置き換えられます。そのパラメータ args
は次のとおりです。
matched: string
. 完全一致g1: string|undefined
. 番号付きグループ 1 のキャプチャg2: string|undefined
. 番号付きグループ 2 のキャプチャoffset: number
. 入力文字列内で一致が見つかった場所input: string
. 入力文字列全体const regexp = /([0-9]{4})-([0-9]{2})/g;
const replacer = (all, year, month) => '|' + all + '|';
.equal(
assert'a 1995-12 b'.replaceAll(regexp, replacer),
'a |1995-12| b');
名前付きキャプチャグループ (ES2018) もサポートされています。存在する場合、キャプチャを含むオブジェクトのプロパティを持つ引数が最後に追加されます
const regexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})/g;
const replacer = (...args) => {
const groups=args.pop();
return '|' + groups.month + '|';
;
}.equal(
assert'a 1995-12 b'.replaceAll(regexp, replacer),
'a |12| b');
.replace(searchValue: string | RegExp, replaceValue: string): string
[ES3]
.replace(searchValue: string | RegExp, replacer: (...args: any[]) => string): string
[ES3]
.replace()
は .replaceAll()
のように機能しますが、searchValue
が文字列または /g
のない正規表現の場合、最初に出現したもののみに置き換えられます
> 'x.x.'.replace('.', '#')'x#x.'
> 'x.x.'.replace(/./, '#')'#.x.'
このメソッドの詳細については、§43.6.8.1 “str.replace(searchValue, replacementValue)
[ES3]” を参照してください.
.toUpperCase(): string
[ES1]
すべて小文字のアルファベット文字が大文字に変換された文字列のコピーを返します. さまざまなアルファベットでどの程度うまく機能するかは、JavaScript エンジンによって異なります.
> '-a2b-'.toUpperCase()'-A2B-'
> 'αβγ'.toUpperCase()'ΑΒΓ'
.toLowerCase(): string
[ES1]
すべて大文字のアルファベット文字が小文字に変換された文字列のコピーを返します. さまざまなアルファベットでどの程度うまく機能するかは、JavaScript エンジンによって異なります.
> '-A2B-'.toLowerCase()'-a2b-'
> 'ΑΒΓ'.toLowerCase()'αβγ'
.trim(): string
[ES5]
先頭と末尾のすべての空白(スペース、タブ、行末記号など)が削除された文字列のコピーを返します。
> '\r\n#\t '.trim()'#'
> ' abc '.trim()'abc'
.trimEnd(): string
[ES2019]
.trim()
と似ていますが、文字列の末尾のみがトリミングされます
> ' abc '.trimEnd()' abc'
.trimStart(): string
[ES2019]
.trim()
と似ていますが、文字列の先頭のみがトリミングされます
> ' abc '.trimStart()'abc '
**演習: 文字列メソッドの使用**
exercises/strings/remove_extension_test.mjs
**クイズ**
クイズアプリ を参照してください。