JavaScript文字列は不変ですか?JavaScriptで「文字列ビルダー」が必要ですか?


237

JavaScriptは不変または可変の文字列を使用しますか?「文字列ビルダー」は必要ですか?


3
はい、yは不変であり、何らかの「文字列ビルダー」が必要です。このblog.codeeffects.com/Article/String-Builder-In-Java-Scriptまたはこのcodeproject.com/KB/scripting/stringbuilder.aspx
Kizz

2
興味深いことに、これらの例は私の答えの私の発見と矛盾しています。
ファンメンデス

回答:


294

それらは不変です。文字列内の文字をなどのように変更することはできませんvar myString = "abbdef"; myString[2] = 'c'。などの文字列操作メソッドはtrimslice新しい文字列を返します。

同じように、同じ文字列への2つの参照がある場合、一方を変更しても他方には影響しません

let a = b = "hello";
a = a + " world";
// b is not affected

ただし、私はいつもAshが彼の回答で述べたことを聞いたことがあります(Array.joinを使用すると連結の方が速い)ので、文字列を連結してStringBuilderに最速の方法を抽象化するさまざまな方法をテストしたいと思いました。これが正しいかどうかを確認するためにいくつかのテストを作成しました(正しくありません)。

これが最速の方法であると私が信じていたものですが、メソッド呼び出しを追加すると遅くなる可能性があると私は考え続けました...

function StringBuilder() {
    this._array = [];
    this._index = 0;
}

StringBuilder.prototype.append = function (str) {
    this._array[this._index] = str;
    this._index++;
}

StringBuilder.prototype.toString = function () {
    return this._array.join('');
}

以下はパフォーマンス速度テストです。3つとも"Hello diggity dog"、空の文字列に10万回連結して構成される巨大な文字列を作成します。

3種類のテストを作成しました

  • 使用するArray.pushと、Array.join
  • を回避するためArray.pushに配列のインデックスを使用してから、Array.join
  • ストレート文字列連結

その後、私はにそれらを抽象化することで、同じ3つのテストを作成しStringBuilderConcatStringBuilderArrayPushそしてStringBuilderArrayIndex http://jsperf.com/string-concat-without-sringbuilder/5そこに行くと私たちは素敵なサンプルを得ることができるので、テストを実行してください。小さなバグを修正したので、テストのデータは消去されました。十分なパフォーマンスデータが得られたら、テーブルを更新します。行くhttp://jsperf.com/string-concat-without-sringbuilder/5古いデータテーブルのために。

リンクをたどらない場合は、いくつかの番号を示します(Ma5rch 2018の最新の更新)。各テストの数値は1000オペレーション/秒です高いほど良い

| Browser          | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988   | 1006 | 2902   | 963     | 1008   | 2902     |
| Firefox 65       | 1979  | 1902 | 2197   | 1917    | 1873   | 1953     |
| Edge             | 593   | 373  | 952    | 361     | 415    | 444      |
| Exploder 11      | 655   | 532  | 761    | 537     | 567    | 387      |
| Opera 58.0.3135  | 1135  | 1200 | 4357   | 1137    | 1188   | 4294     | 

調査結果

  • 今日、すべての常緑樹ブラウザは文字列連結をうまく処理します。Array.joinIE 11のみに役立ちます

  • 全体的に、Operaは最速で、Array.joinの4倍の速度です。

  • Firefoxは2番目でありArray.join、FFではわずかに遅くなりますが、Chromeではかなり遅くなります(3倍)。

  • Chromeは3番目ですが、文字列連結はArray.joinより3倍高速です。

  • StringBuilderを作成しても、パフォーマンスにはあまり影響しないようです。

他の誰かがこれが便利だと思うことを願って

別のテストケース

@RoyTinkerは私のテストに欠陥があると思ったので、同じ文字列を連結して大きな文字列を作成しない新しいケースを作成しました。反復ごとに異なる文字を使用します。文字列の連結は、まだ高速または同じくらい高速に見えました。それらのテストを実行してみましょう。

これをテストする他の方法を常に考えて、以下のさまざまなテストケースに新しいリンクを追加してください。

http://jsperf.com/string-concat-without-sringbuilder/7


@Juan、あなたが私たちに訪問するように要求したリンクは、112文字の文字列を30回連結します。物事のバランスを取るのに役立つ別のテストを次に示します-20,000の異なる 1 文字の文字列でのArray.joinと文字列の連結(結合はIE / FFでははるかに高速です)。 jsperf.com/join-vs-str-concat-large-array
Roy Tinker

1
@RoyTinkerロイ、ああロイ、テストのセットアップで配列を作成しているため、テストは不正行為です。これが、さまざまな文字を使用した実際のテストですjsperf.com/string-concat-without-sringbuilder/7新しいテストケースを自由に作成できますが、配列の作成はテスト自体の一部です
Juan Mendes

@JuanMendes私の目標は、テストケースをjoin文字列連結との厳密な比較に絞り込み、テスト前に配列を構築することでした。その目標が理解されていれば、それはごまかしだとは思いません(またjoin、配列を内部で列挙するためforjoinテストからループを省略してもごまかしはありません)。
ロイティンカー

2
@RoyTinkerはい、そうです。文字列ビルダーは、配列の構築を必要とします。問題は、文字列ビルダーが必要かどうかです。既に文字列が配列内にある場合、ここで説明しているのは有効なテストケースではありません
Juan Mendes

1
@Baltusaj Index / Pushが使用していると言う列Array.join
Juan Mendes

41

サイの本から:

JavaScriptでは、文字列は不変オブジェクトです。つまり、文字列内の文字は変更されない可能性があり、文字列を操作すると実際には新しい文字列が作成されます。文字列は値ではなく参照によって割り当てられます。一般に、オブジェクトが参照によって割り当てられる場合、1つの参照を介してオブジェクトに加えられた変更は、オブジェクトへの他のすべての参照を介して表示されます。ただし、文字列は変更できないため、文字列オブジェクトへの参照を複数持つことができ、文字列の値が知らないうちに変更されることを心配する必要はありません。


7
:サイブックの適切なセクションへのリンクbooks.google.com/...
baudtack

116
Rhinoの本の引用(そしてこの答え)はここでは間違っています。JavaScriptでは、文字列はプリミティブな値型でありオブジェクト(spec)ではありません。実際、ES5の時点では、これらはnull undefined numberおよびとともに5つの値タイプの1つにすぎませんboolean。文字列はによって割り当てられた値しない参照することにより、そのようなものとして渡されます。したがって、文字列は単なる不変ではなく、です。文字列"hello"を次のように変更すること"world"は、これから数3が数4であると決定するようなものです...意味がありません。
Benjamin Gruenbaum 2013

8
はい、私のコメントのように、文字列不変ですが、それらは参照型でもオブジェクトでもありません-プリミティブ値型です。彼らはどちらもじゃないんだ参照する簡単な方法は、文字列にプロパティを追加し、それを読もうとすることです:var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
ベンジャミンGruenbaum

9
@ VidarS.Ramdalいいえ、String文字列コンストラクタを使用して作成されたオブジェクトは、JavaScript文字列値のラッパーです。ボックス化された型の文字列値には、.valueOf()関数を使用してアクセスできます。これは、Numberオブジェクトと数値にも当てはまります。Stringを使用しnew Stringて作成されたオブジェクトは実際の文字列ではなく、文字列のラッパーまたはボックスであることに注意することが重要です。es5.github.io/#x15.5.2.1を参照してください。オブジェクトへの変換方法については、es5.github.io /#x9.9を
Benjamin

5
一部の人々が文字列はオブジェクトであると言う理由に関して、それらはおそらくPythonまたはLispまたはその他の言語から来ています。その仕様では、「オブジェクト」という単語を使用してあらゆる種類のデータ(整数を含む)を意味します。彼らは、ECMA仕様が単語「オブジェクト型のメンバー」をどのように定義するかを読む必要があるだけです。また、「値」という言葉でも、言語の仕様によって意味が異なる場合があります。
Jisang Yoo 2014

21

パフォーマンスのヒント:

大きな文字列を連結する必要がある場合は、文字列部分を配列Array.Join()に入れ、メソッドを使用して文字列全体を取得します。これは、多数の文字列を連結する場合、何倍も速くなる可能性があります。

StringBuilderJavaScriptにはありません。


stringBuilderがないこと、msAjaxにはそれがあることを知っています。msAjaxが便利かどうかを考えていました
DevelopingChris

5
これは文字列が不変であるかどうかに関係していますか?
baudtack 09/06/06

4
@docgnome:文字列は不変であるため、文字列の連結では、Array.joinアプローチよりも多くのオブジェクトを作成する必要があります
Juan Mendes

8
上記のフアンのテストによると、文字列の連結は実際にはIEとChromeの両方で高速ですが、Firefoxでは低速です。
ビル・ヤン

9
あなたの答えを更新することを検討してください、それはずっと前に本当だったかもしれませんが、それはもうありません。jsperf.com/string-concat-without-sringbuilder/5を
フアンメンデス

8

私のような単純な心を明確にするために(MDNから):

不変とは、オブジェクトが作成されると状態を変更できないオブジェクトです。

文字列と数値は不変です。

不変とは、次のことを意味します。

変数名を新しい値を指すようにすることができますが、以前の値は引き続きメモリに保持されます。したがって、ガベージコレクションの必要性。

var immutableString = "Hello";

//上記のコードでは、文字列値を持つ新しいオブジェクトが作成されます。

immutableString = immutableString + "World";

//ここで、既存の値に「World」を追加します。

これは文字列「immutableString」を変更しているようですが、変更していません。代わりに:

「immutableString」に文字列値を追加すると、次のイベントが発生します。

  1. 「immutableString」の既存の値が取得されます
  2. 「ワールド」は「immutableString」の既存の値に追加されます
  3. 次に、結果の値が新しいメモリブロックに割り当てられます。
  4. 「immutableString」オブジェクトが新しく作成されたメモリ空間を指すようになりました
  5. 以前に作成されたメモリ空間がガベージコレクションに使用できるようになりました。

4

文字列型の値は不変です、String()コンストラクタを使用して作成されたStringオブジェクトは変更可能です。これはオブジェクトであり、新しいプロパティを追加できるためです。

> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }

一方、新しいプロパティを追加することはできますが、既存のプロパティを変更することはできません

Chromeコンソールでのテストのスクリーンショット

結論として、1。すべての文字列型の値(プリミティブ型)は不変です。2. Stringオブジェクトは変更可能ですが、それに含まれる文字列型の値(プリミティブ型)は不変です。


JavaScript文字列オブジェクトは不変のdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
prasun

@prasunですが、そのページには、「オブジェクトを除くすべての型は不変値(値は変更できない値)を定義しています。」と記述されています。文字列オブジェクトはオブジェクトです。そして、新しいプロパティを追加できる場合、それはどのように不変ですか?
zhanziyang

「文字列型」のセクションを読んでください。JavaScriptの文字列リンクは、プリミティブとオブジェクトの両方を指し、後で「JavaScript文字列は不変」と表示されます。このトピックは2つの異なるメモで矛盾するため、ドキュメントは明確ではないようです
prasun

6
new String不変文字列の周りに可変ラッパーを生成します
tomdemuyt

1
上記の@zhanziyangのコードを実行することで、非常に簡単にテストできます。新しいプロパティをStringオブジェクト(ラッパー)に完全に追加できます。つまり、それは不変ではありません(デフォルトでは、他のオブジェクトと同様に呼び出しObject.freezeて不変にすることができます)。ただし、プリミティブな文字列値型は、Stringオブジェクトラッパーに含まれているかどうかにかかわらず、常に不変です。
マークリード

3

文字は不変です。文字列は変更できません。新しい文字列しか作成できません。

例:

var str= "Immutable value"; // it is immutable

var other= statement.slice(2, 10); // new string

1

ASP.NET AjaxのStringBuilderに関する(Ashの応答へのコメントでの)質問に関して、専門家はこれに反対しているようです。

Christian Wenzは、彼の著書 『Programming ASP.NET AJAX(O'Reilly)』で、「このアプローチはメモリに測定可能な影響を与えません(実際、実装は標準のアプローチよりもティックが遅いようです)。

一方、Galloらは彼らの著書ASP.NET AJAX in Action(Manning)で、「連結する文字列の数が多い場合、文字列ビルダーはパフォーマンスの大幅な低下を回避するための重要なオブジェクトになる」と述べています。

独自のベンチマークを行う必要があると思います。ブラウザによって結果も異なる可能性があります。ただし、パフォーマンスが向上しない場合でも、C#やJavaなどの言語でStringBuilderを使用してコーディングすることに慣れているプログラマにとっては、「有用」であると考えられます。


1

それは遅い投稿ですが、私は答えの中に良い本の引用を見つけられませんでした。

信頼できる本を除いて、これは明確です。

文字列はECMAScriptでは不変です。つまり、文字列はいったん作成されると、その値を変更できません。変数が保持する文字列を変更するには、元の文字列を破棄し、変数に新しい値を含む別の文字列を入力する必要があります... —Web開発者向けのプロフェッショナルJavaScript、第3版、p.43

さて、Rhinoの本の抜粋を引用する答えは、文字列の不変性については正しいですが、「文字列は値ではなく参照によって割り当てられます」と間違って言っています。(おそらく彼らはもともと言葉を逆に置くつもりでした)。

"参照/値"の誤解は、 "Professional JavaScript"の章の "Primitive and Reference values"で明確にされています。

5つのプリミティブタイプ... [あり]:未定義、Null、ブール、数値、文字列。これらの変数は、変数に格納されている実際の値を操作しているため、値によってアクセスされるといいます。 —Web開発者向けのプロフェッショナルJavaScript、第3版、p.85

それはオブジェクトとは反対です

オブジェクトを操作するときは、実際のオブジェクト自体ではなく、実際にそのオブジェクトへの参照に取り組んでいます。このため、このような値は参照によってアクセスされると言われています。—Web開発者向けのプロフェッショナルJavaScript、第3版、p.85


FWIW:Rhinoの本はおそらく、文字列割り当ての内部/実装が(文字列の内容をコピーするのではなく)ポインタを格納/コピーすることを意味します。その後の文章によると、彼らにとっては事故ではないようです。しかし、私は同意します。「参照により」という用語を誤用しています。実装が(パフォーマンスのために)ポインターを渡すからといって、「参照による」ものではありません。Wiki-評価戦略は、このトピックに関する興味深い読み物です。
ToolmakerSteve


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.