回答:
つまり、オブジェクトをインスタンス化すると、そのプロパティを変更できなくなります。最初のアラートでは、fooを変更していません。新しい文字列を作成しています。これが、2番目のアラートでooではなく「foo」と表示される理由です。
文字列のメソッドを呼び出すと、変更された文字列が返されますが、最初の文字列は変更されません。
はい。いったん作成された文字列を変更することはできません。これは、新しい文字列オブジェクトをstr
変数に割り当てることができないという意味ではありません。strが参照する現在のオブジェクトを変更することはできません。
文字列が変更可能だった場合、それは2番目のalert()がooも返すことを意味しますか?
技術的には、サブストリングメソッドは新しいストリングを返すため、違います。オブジェクトを変更可能にしても、メソッドは変更されません。変更可能にすることは、技術的には、部分文字列が新しい文字列を作成する代わりに元の文字列を変更するように作成できることを意味します。
下位レベルでは、不変性は、文字列が格納されているメモリが変更されないことを意味します。文字列を作成すると"foo"
、値を格納するためのメモリが割り当てられます"foo"
。このメモリは変更されません。たとえばで文字列を変更すると、substr(1)
新しい文字列が作成され、を格納するメモリの別の部分が割り当てられ"oo"
ます。これで、2つの文字列がメモリに"foo"
あり"oo"
ます。使用し"foo"
なくなったとしても、ガベージコレクションが行われるまで残ります。
文字列操作が比較的高価な理由の1つ。
不変とは、変更または修正できないことを意味します。
したがって、文字列に値を割り当てると、この値は置き換えられるのではなく、最初から作成されます。したがって、新しい文字列が同じ文字列に割り当てられるたびに、コピーが作成されます。したがって、実際には、元の値を変更することはありません。
JavaScriptについては定かではありませんが、Javaでは、文字列は「文字列定数プール」を使用して不変性に追加の手順を実行します。文字列は、文字列リテラル("foo"
)またはString
クラスコンストラクターを使用して構築できます。文字列リテラルで構築された文字列は文字列定数プールの一部であり、同じ文字列リテラルは常にプールからの同じメモリアドレスになります。
例:
String lit1 = "foo";
String lit2 = "foo";
String cons = new String("foo");
System.out.println(lit1 == lit2); // true
System.out.println(lit1 == cons); // false
System.out.println(lit1.equals(cons)); // true
上記では、lit1
とlit2
は同じ文字列リテラルを使用して構築されているため、同じメモリアドレスを指しています。lit1 == lit2
結果true
はになります。これらはまったく同じオブジェクトだからです。
ただし、cons
クラスコンストラクターを使用して構築されます。パラメータは、同じ文字列定数ではあるが、新しいメモリのコンストラクタ割り当てはcons
、意味はcons
同じオブジェクトではありませんlit1
し、lit2
同じデータを含むにもかかわらず、。
もちろん、3つの文字列にはすべて同じ文字データが含まれているため、このequals
メソッドを使用するとtrueが返されます。
(もちろん、どちらのタイプの文字列構成も不変です)
可変性の教科書定義は、責任を負うものであり、変更または変更されることがあります。プログラミングでは、時間とともに状態が変化することが許可されているオブジェクトを意味する言葉を使用します。不変の値は正反対です。作成後は変更できません。
これが奇妙に思われる場合は、私たちがいつも使用している値の多くが実際には不変であることを思い出させてください。
var statement = "I am an immutable value";
var otherStr = statement.slice(8, 17);
私は、2行目がステートメントの文字列を決して変更しないことを知って驚く人はいないと思います。実際、操作する文字列を変更する文字列メソッドはなく、すべて新しい文字列を返します。その理由は、文字列は不変であるためです。文字列は変更できません。新しい文字列しか作成できません。
JavaScriptに組み込まれる不変の値は文字列だけではありません。数値も不変です。2 + 3という式を評価すると、数値2の意味が変わる環境を想像できますか?ばかげたことに聞こえるかもしれませんが、オブジェクトと配列を常に使用しています。
文字列からスタックへ... Eric Lippertのブログから取られた、わかりやすい例:
System.Collections.Generic.Stackのような変更可能なスタックは明らかに適切ではありません。既存のパスを取得して、最後の要素のすべての隣接要素に対して新しいパスを作成できるようにしたいのですが、新しいノードを標準スタックにプッシュすると、スタックが変更されます。プッシュする前にスタックのコピーを作成する必要があります。これは、その内容のすべてを不必要に複製することになるため、ばかげています。
不変スタックにはこの問題はありません。不変スタックをプッシュすると、テールとして古いスタックにリンクする新しいスタックが作成されるだけです。スタックは不変なので、他のコードが一緒になってテールの内容をいじる危険はありません。古いスタックを心ゆくまで使い続けることができます。
不変性の理解を深めるには、この記事から始まるEricの投稿を読んでください。