文字列の新しいメソッド

この章では、文字列オブジェクトの新しいメソッドを紹介します。

String.fromCodePoint()

ES5 は、Unicode コード ポイントから対応する文字を返すための String.fromCharCode() メソッドを提供しますが、このメソッドは 0xFFFF より大きいコード ポイントを持つ文字を認識できません。

String.fromCharCode(0x20BB7)
//「ஷ」

上記のコードでは、「String.fromCharCode()」は「0xFFFF」より大きいコード ポイントを認識できないため、「0x20BB7」がオーバーフローし、最上位ビットの「2」が破棄され、最終的に「U+0BB7」に対応するコード ポイントを返します。コードポイント「U+20BB7」に対応する文字ではなく、文字です。

ES6 は、「0xFFFF」より大きい文字を識別できる「String.fromCodePoint()」メソッドを提供し、「String.fromCharCode()」メソッドの欠点を補います。実際には、これは以下の codePointAt() メソッドのまったく逆です。

String.fromCodePoint(0x20BB7)
// "𠮷"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// 真実

上記のコードでは、String.fromCodePoint メソッドに複数のパラメータがある場合、それらは 1 つの文字列に結合されて返されます。

「fromCodePoint」メソッドは「String」オブジェクトで定義されているのに対し、「codePointAt」メソッドは文字列のインスタンス オブジェクトで定義されていることに注意してください。

String.raw()

ES6 は、ネイティブ String オブジェクト用の「raw()」メソッドも提供します。このメソッドは、すべてのスラッシュがエスケープされた (つまり、スラッシュの前にスラッシュが追加された) 文字列を返し、テンプレート文字列の処理によく使用されます。

String.raw`こんにちは\n${2+3}!`
// 実際の戻り値は「Hi\\n5!」であり、エスケープされた結果は「Hi\n5!」と表示されます。

String.raw`こんにちは\u000A!`;
// 実際の戻り値は「Hi\\u000A!」であり、エスケープされた結果は「Hi\u000A!」と表示されます。

元の文字列内のスラッシュがエスケープされている場合、 String.raw() はそれらを再度エスケープします。

String.raw`こんにちは\\n`
// 「こんにちは\\\\n」を返します

String.raw`Hi\\n` === "Hi\\\\n" // true

String.raw() メソッドは、テンプレート文字列を処理するための基本的なメソッドとして使用でき、すべての変数を置換し、スラッシュをエスケープして、次のステップで文字列として使用できるようにします。

String.raw() は本質的には通常の関数であり、テンプレート文字列専用のタグ付き関数にすぎません。通常の関数の形式で記述する場合、その最初のパラメータは「raw」属性を持つオブジェクトである必要があり、「raw」属性の値はテンプレート文字列の解析された値に対応する配列である必要があります。

// `foo${1 + 2}バー`
// と同等
String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"

上記のコードでは、String.raw() メソッドの最初のパラメータはオブジェクトであり、その raw プロパティは元のテンプレート文字列を解析した後に取得される配列と同等です。

関数としての String.raw() のコード実装は基本的に次のようになります。

String.raw = 関数 (文字列, ...値) {
  出力 = '';
  インデックスを付けます。
  for (インデックス = 0; インデックス < 値.長さ; インデックス++) {
    出力 += 文字列.生[インデックス] + 値[インデックス];
  }

  出力 += 文字列.raw[インデックス]
  出力を返します。
}

インスタンスメソッド: codePointAt()

JavaScript の内部では、文字は UTF-16 形式で保存され、各文字は 2 バイトに固定されています。格納するのに「4」バイトを必要とする文字 (「0xFFFF」より大きい Unicode コード ポイントを持つ文字) の場合、JavaScript はそれらを 2 文字として扱います。

var s = "𠮷";

s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

上記のコードでは、漢字「𠮷」 (この文字は「縁起の良い」の「縁起」ではないことに注意してください) のコード ポイントは 0x20BB7、UTF-16 エンコードは 0xD842 0xDFB7 (10 進数は55362 57271)、これには 4 バイトを保存する必要があります。この 4 文字の場合、JavaScript は文字列長が 2 と誤って判断され、charAt() メソッドは文字全体を読み取ることができず、charCodeAt() メソッドはそれを読み取ることしかできません。最初の 2 バイトと最後の 2 バイトの値を別々に返します。

ES6 は、4 バイトに格納された文字を正しく処理し、文字のコード ポイントを返すことができる codePointAt() メソッドを提供します。

let s = '𠮷a';

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271

s.codePointAt(2) // 97

codePointAt() メソッドのパラメータは、文字列内の文字の位置 (0 から始まります) です。上記のコードでは、JavaScript は「𠮷a」を 3 文字として扱います。 codePointAt メソッドは最初の文字の「𠮷」を正しく識別し、その 10 進コード ポイント 134071 (つまり、16 進数の 20BB7 ) を返します。 2 番目の文字 (つまり、"𠮷" の最後の 2 バイト) と 3 番目の文字 "a" では、codePointAt() メソッドの結果は charCodeAt() メソッドと同じになります。

つまり、codePointAt() メソッドは 32 ビット UTF-16 文字コード ポイントを正しく返します。 2 バイトで格納された通常の文字の場合、戻り結果は charCodeAt() メソッドと同じになります。

codePointAt() メソッドはコード ポイントの 10 進数値を返します。16 進数値が必要な場合は、toString() メソッドを使用して変換できます。

let s = '𠮷a';

s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"

codePointAt() メソッドのパラメータがまだ間違っていることに気づいたかもしれません。たとえば、上記のコードでは、文字列 s 内の文字 a の正しい位置番号は 1 である必要がありますが、2 を codePointAt() メソッドに渡す必要があります。この問題に対する 1 つの解決策は、「for...of」 ループを使用することです。これにより、32 ビット UTF-16 文字が正しく認識されます。

let s = '𠮷a';
for (s を ch にします) {
  console.log(ch.codePointAt(0).toString(16));
}
//20bb7
// 61

もう 1 つの方法は、スプレッド演算子 (...) を使用して展開操作を実行することです。

let arr = [...'𠮷a'] // arr.length === 2
arr.forEach(
  ch => console.log(ch.codePointAt(0).toString(16))
);
//20bb7
// 61

codePointAt() メソッドは、文字が 2 バイトで構成されているか 4 バイトで構成されているかをテストする最も簡単な方法です。

関数は32Bit(c) {
  c.codePointAt(0) > 0xFFFF を返します。
}

is32Bit("𠮷") // true
is32Bit("a") // false

インスタンスメソッド:normalize()

多くのヨーロッパ言語にはイントネーション記号とアクセント記号があります。これらを表現するために、Unicode には 2 つのメソッドが用意されています。 1 つは、Ƒ (\u01D1) などのアクセント付き文字を直接指定する方法です。もう 1 つは結合文字を提供することです。つまり、オリジナルの文字とアクセント記号を合成して、O (\u004F) や ˇ (\u030C) を結合して Ƒ にするなどです。 (\u004F\u030C)。

これら 2 つの表現は視覚的にも意味的にも同等ですが、JavaScript では認識されません。

'\u01D1'==='\u004F\u030C' //false

'\u01D1'.length // 1
'\u004F\u030C'.length // 2

上記のコードは、JavaScript が複合文字を 2 つの文字として扱うため、2 つの表現方法が等しくないことを示しています。

ES6 は、文字の異なる表現を同じ形式に統合するための文字列インスタンスの normalize() メソッドを提供します。これは Unicode 正規化と呼ばれます。

'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// 真実

normalize メソッドは、normalize メソッドを指定するパラメータを受け取ることができます。パラメータのオプションの値は次の 4 つです。

  • デフォルトパラメータの「NFC」は「正規化形式正規合成」(正規化形式正規合成)を表し、複数の単純な文字の合成文字を返します。いわゆる「標準等価性」とは、視覚的および意味的等価性を指します。
  • NFD、これは「正規化形式正準分解」を意味します。つまり、標準等価性の前提の下で、複合文字分解の複数の単純な文字が返されます。
  • NFKC は、「正規化形式互換構成」(Normalization Form Compatibility Comboposition) を意味し、複合文字を返します。いわゆる「互換性のある同等性」とは、「囍」と「西渓」のように、意味上の同等性はあるが、視覚的な同等性がないことを意味します。 (これは単なる例であり、「normalize」メソッドは中国語を認識できません。)
  • NFKD は、「正規化形式互換性分解」を意味します。つまり、互換性と等価性を前提として、複合文字分解の複数の単純な文字が返されます。
'\u004F\u030C'.normalize('NFC').length // 1
'\u004F\u030C'.normalize('NFD').length // 2

上記のコードは、「NFC」パラメータが文字の合成形式を返し、「NFD」パラメータが文字の分解形式を返すことを示しています。

ただし、「normalize」メソッドは現在、3 文字以上の組み合わせを認識しません。この場合でも、正規表現を使用し、Unicode の数値間隔で判断することしかできません。

インスタンスメソッド: include()、startsWith()、endsWith()

従来、JavaScript には、文字列が別の文字列内に含まれているかどうかを判断するために使用できる「indexOf」メソッドしかありませんでした。 ES6 では 3 つの新しいメソッドが提供されます。

  • includes(): パラメータ文字列が見つかったかどうかを示すブール値を返します。
  • startsWith(): パラメータ文字列が元の文字列の先頭にあるかどうかを示すブール値を返します。
  • endsWith(): パラメータ文字列が元の文字列の末尾にあるかどうかを示すブール値を返します。
let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

3 つのメソッドはすべて、検索を開始する位置を示す 2 番目のパラメーターをサポートしています。

let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上記のコードは、2 番目のパラメーター n を使用する場合、endsWith が他の 2 つのメソッドとは異なる動作をすることを示しています。これは最初の「n」文字を対象としますが、他の 2 つのメソッドは文字列の終わりまでの「n」番目の位置を対象とします。

インスタンスメソッド:repeat()

repeat メソッドは新しい文字列を返します。これは、元の文字列を n 回繰り返すことを意味します。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "ハローハロー"
'な'.repeat(0) // ""

パラメータが小数の場合は四捨五入されます。

'な'.repeat(2.9) // "なな"

repeat のパラメータが負の数または Infinity の場合、エラーが報告されます。

'な'.repeat(無限大)
// 範囲エラー
'な'.repeat(-1)
// 範囲エラー

ただし、引数が 0 から -1 までの小数の場合は、丸め演算が最初に実行されるため、0 と同等になります。 0 と -1 の間の小数は四捨五入後の '-0' に等しく、'repeat' は 0 とみなされます。

'な'.repeat(-0.9) // ""

パラメータ NaN は 0 と同等です。

'na'.repeat(NaN) // ""

「repeat」のパラメータが文字列の場合は、まず数値に変換されます。

'な'.repeat('な') // ""
'na'.repeat('3') // "ナナナ"

インスタンスメソッド:padStart()、padEnd()

ES2017では文字列補完長の機能が導入されました。文字列の長さが足りない場合は、先頭または末尾で完成します。 padStart() は先頭の補完に使用され、padEnd() は末尾の補完に使用されます。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

上記のコードでは、padStart()padEnd() は合計 2 つのパラメーターを受け入れます。最初のパラメーターは文字列補完が有効になる最大長で、2 番目のパラメーターは補完に使用される文字列です。

元の文字列の長さが最大長以上の場合、文字列補完は有効にならず、元の文字列が返されます。

'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'

補完に使用する文字列と元の文字列の長さの合計が最大長を超える場合、桁数を超える補完文字列は切り捨てられます。

'abc'.padStart(10, '0123456789')
// '0123456abc'

2 番目のパラメータを省略した場合、デフォルトで長さを補完するためにスペースが使用されます。

'x'.padStart(4) // 'x'
'x'.padEnd(4) // 'x '

padStart() の一般的な使用法は、数値補完の桁数を指定することです。次のコードは、10 桁の数値文字列を生成します。

'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

もう 1 つの用途は、ヒント文字列の書式設定です。

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

インスタンスメソッド:trimStart()、trimEnd()

ES2019 2 つの新しいメソッド trimStart()trimEnd() を文字列インスタンスに追加しました。それらの動作は trim() と一致しており、 trimStart() は文字列の先頭のスペースを削除し、 trimEnd() は文字列の末尾のスペースを削除します。新しい文字列を返し、元の文字列は変更しません。

const s = 'abc';

s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // "abc"

上記のコードでは、trimStart() は先頭のスペースのみを削除し、末尾のスペースを保持します。 trimEnd() も同様に動作します。

この 2 つの方法は、スペースバー以外にも、タブキーや文字列の先頭(または末尾)の改行文字などの目に見えない空白記号にも有効です。

ブラウザは 2 つの追加メソッドも展開します。trimLeft()trimStart() のエイリアスであり、trimRight()trimEnd() のエイリアスです。

インスタンスメソッド: matchAll()

matchAll() メソッドは、現在の文字列内の正規表現に一致するものをすべて返します。詳細については、「正規拡張機能」の章を参照してください。

インスタンスメソッド: replaceAll()

これまで、String インスタンス メソッド replace() は最初に一致したもののみを置き換えることができました。

'aabbcc'.replace('b', '_')
// 'aa_bcc'

上の例では、replace() は最初の b をアンダースコアに置き換えるだけです。

すべての一致を置換したい場合は、正規表現の g 修飾子を使用する必要があります。

'aabbcc'.replace(/b/g, '_')
// 'aa__cc'

結局のところ、正規表現はそれほど便利で直感的ではありません。ES2021 では、すべての一致を一度に置換できる replaceAll() メソッドが導入されています。

'aabbcc'.replaceAll('b', '_')
// 'aa__cc'

使い方は replace() と同じで、元の文字列を変更せずに新しい文字列を返します。

String.prototype.replaceAll(searchValue, 置換)

上記のコードでは、searchValue は検索パターンであり、文字列またはグローバル正規表現 (g 修飾子を含む) にすることができます。

searchValueg 修飾子のない正規表現である場合、replaceAll() はエラーを報告します。これは「replace()」とは異なります。

// エラーを報告しません
'aabbcc'.replace(/b/, '_')

// エラーを報告する
'aabbcc'.replaceAll(/b/, '_')

上の例では、/b/g 修飾子がないため、replaceAll() はエラーを報告します。

replaceAll() の 2 番目のパラメータ replacement は置換テキストを表す文字列であり、いくつかの特別な文字列を使用できます。

  • $&: 一致した文字列。
  • $`: 結果の前のテキストと一致します。
  • $': 結果の後のテキストと一致します。
  • $n: 一致に成功したコンテンツの n 番目のグループ。 n は 1 から始まる自然数です。このパラメータを有効にするための前提条件は、最初のパラメータが正規表現である必要があることです。
  • $$: ドル記号 $ を指します。

以下にいくつかの例を示します。

// $& は一致した文字列、つまり `b` 自体を表します
// したがって、返された結果は元の文字列と一致します。
'abbc'.replaceAll('b', '$&')
// 'abbc'

// $` は一致結果の前の文字列を表します
// 最初の `b` については、$` は `a` を参照します
// 2 番目の `b` については、$` は `ab` を参照します
'abbc'.replaceAll('b', '$`')
// 'aabc'

// $' は一致結果の後の文字列を表します
// 最初の `b` については、$' は `bc` を参照します
// 2 番目の `b` については、$' は `c` を参照します
'abbc'.replaceAll('b', `$'`)
// 'abcc'

// $1 は正規表現の最初のグループ一致を表し、`ab` を参照します
// $2 は正規表現の 2 番目のグループ一致を表し、`bc` を参照します
'abbc'.replaceAll(/(ab)(bc)/g, '$2$1')
//'bcab'

// $$ は $ を参照します
'abc'.replaceAll('b', '$$')
// 'a$c'

replaceAll() の 2 番目のパラメータ replacement は、文字列であるだけでなく、関数にすることもできます。関数の戻り値は、最初のパラメータ searchValue に一致するテキストを置き換えます。

'aabbcc'.replaceAll('b', () => '_')
// 'aa__cc'

上記の例では、replaceAll() の 2 番目のパラメーターは関数であり、関数の戻り値は b のすべての一致を置き換えます。

この置換関数は複数のパラメータを受け入れることができます。最初のパラメータはキャプチャされた一致内容で、2 番目のパラメータはグループ一致をキャプチャします (グループ一致の数と同じ数の対応するパラメータがあります)。さらに、最後に 2 つのパラメータを追加できます。最後から 2 番目のパラメータは文字列全体でキャプチャされたコンテンツの位置であり、最後のパラメータは元の文字列です。

const str = '123abc456';
const regex = /(\d+)([a-z]+)(\d+)/g;

関数 replacer(一致、p1、p2、p3、オフセット、文字列) {
  return [p1, p2, p3].join(' - ');
}

str.replaceAll(正規表現、置換子)
// 123 - abc - 456

上記の例では、正規表現には 3 つのグループ一致があるため、replacer() 関数の最初のパラメータ match はキャプチャされた一致するコンテンツ (つまり、文字列 123abc456) であり、次の 3 つのパラメータ p1 は、「p2」および「p3」は、3 つのグループに連続して一致します。

インスタンスメソッド: at()

at() メソッドはパラメータとして整数を受け取り、パラメータで指定された位置にある文字を返します。負のインデックス (つまり、逆数の位置) をサポートします。

const str = 'こんにちは';
str.at(1) // "e"
str.at(-1) // "o"

引数の位置が文字列の範囲を超える場合、 at()unknown を返します。

このメソッドは、配列に追加された at() メソッドから来ています。これはまだ第 3 段階の提案です。「配列」の章の概要を参照してください。

インスタンスメソッド: toWellFormed()

ES2024 では、Unicode サロゲートを処理するための新しい文字列メソッド toWellFormed() が導入されています。

JavaScript 言語は内部で UTF-16 形式を使用して各文字を表します。 UTF-16 には 16 ビットしかなく、「U+0000」と「U+FFFF」の間のコード ポイントを持つ Unicode 文字のみを表現できます。 U+FFFF より大きいコード ポイントを持つ Unicode 文字 (つまり、16 ビットより大きいコード ポイントを持つ文字、U+10000 から U+10FFFF) の場合、解決策はサロゲート文字ペアを使用することです。文字の組み合わせで表される 2 つの UTF-16 を使用します。

具体的には、UTF-16 では、「U+D800」から「U+DFFF」は空の文字フィールドであり、サロゲート文字ペア専用に予約されていると規定されています。この範囲内のコード ポイントに遭遇すると、それがサロゲート文字のペアであることがわかります。サロゲート文字のペアは、それ自体では意味がなく、解釈するには 2 つの文字と組み合わせる必要があります。このうち、前者の文字の範囲は 0xD800 から 0xDBFF として指定され、後者の文字の範囲は 0xDC00 から 0xDFFF として指定されます。たとえば、コード ポイント U+1D306 に対応する文字は 𝌆 で、UTF-16 では 0xD834 0xDF06 と記述されます。

ただし、文字列内に 1 つのサロゲート文字ペア、つまり U+D800 から U+DFFF の文字が出現する可能性があり、これには別の一致する文字がないため解釈できず、さまざまな状況が発生します。

.toWellFormed() はこの問題を解決するもので、元の文字列を変更せずに新しい文字列を返し、元の文字列内の 1 つのサロゲート文字ペアを U+FFFD に置き換えます。任意の通常 文字列を処理する関数で使用されます。

"ab\uD800".toWellFormed() // 'ab�'

上の例では、「\uD800」は単一のサロゲート文字ペアであり、単独で使用すると意味がありません。 toWellFormed() は、この文字を \uFFFD に変換します。

以下の例を見ると、「encodeURI()」は単一のサロゲート文字ペアに遭遇するとエラーを報告します。

const illFormed = "https://example.com/search?q=\uD800";

encodeURI(illFormed) // エラー

toWellFormed() を使用して形式を変換した後、encodeURI() を使用してもエラーは報告されません。

const illFormed = "https://example.com/search?q=\uD800";

encodeURI(illFormed.toWellFormed()) // 正しい

作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0