突然変異は変化です。 形の変化または本質の変化。 変異するものは変更される可能性があります。 突然変異の性質をよりよく理解するために、映画「X-メン」のヒーローについて考えてください。 彼らは突然素晴らしい機会を得ることができました。 ただし、問題は、これらの可能性がいつ現れるかが正確にわからないことです。 あなたの同志が理由もなく青くなり、ウールで生い茂ったと想像してください。 怖いですよね? JavaScriptにも同じ問題があります。 コードが突然変異の影響を受ける場合、これは非常に予想外に、何かを変更したり壊したりできることを意味します。

JavaScriptオブジェクトと突然変異
JavaScriptオブジェクトにプロパティを追加できます。 オブジェクトのインスタンス化後にこれが行われると、オブジェクトは不可逆的に変更されます。 彼はX-Menのキャラクターの1人のように突然変異します。
次の例では、オブジェクトである定数
eggは、
isBrokenプロパティが追加された後に
isBrokenます。 そのようなオブジェクト(
eggような)を変更可能(つまり、変更、変更の能力がある)と呼びます。
const egg = { name: "Humpty Dumpty" }; egg.isBroken = false; console.log(egg); // { // name: "Humpty Dumpty", // isBroken: false // }
JavaScriptでは、突然変異は非常に一般的です。 あなたは文字通り常にどこでもそれらに直面することができます。
突然変異の危険性について
eggオブジェクトが書き込まれる
newEggという名前の定数を作成するとします。 次に、
newEgg nameプロパティを変更する必要があり
newEgg 。
const egg = { name: "Humpty Dumpty" }; const newEgg = egg; newEgg.name = "Errr ... Not Humpty Dumpty";
newEggを変更(オブジェクトを変更)
newEgg 、
eggも自動的に変更されます。 それについてご存知ですか?
console.log(egg); // { // name: "Errr ... Not Humpty Dumpty" // }
上記の例は、突然変異の危険性を示しています。 コード内の何かを変更すると、他の場所にあるものも変更される可能性があり、そのため、あなたもそれを知ることはできません。 その結果、検出および修正が困難なエラー。
これらの奇妙な点はすべて、JavaScriptのオブジェクトが参照によって渡されるという事実の結果です。
JavaScriptのオブジェクトとそれらへの参照
「オブジェクトは参照渡し」というステートメントの意味を理解するには、まずJavaScriptの各オブジェクトに一意の識別子があることを理解する必要があります。 オブジェクトを変数に割り当てる場合、オブジェクトの値を変数に書き込んでコピーする代わりに、変数をそのオブジェクトの識別子にバインドします(つまり、変数はオブジェクトを参照するようになります)。 そのため、2つの異なるオブジェクトを比較し、同じ値を含む(またはまったく含まない)場合でも、
falseになり
false 。
console.log({} === {}); // false
上記の例で、
egg定数が
newEgg定数に割り当てられた
newEgg 、
egg定数によって参照される同じオブジェクトへのリンクが
newEggに書き込まれ
newEgg 。
eggと
newEggは同じオブジェクトを参照するため、
newEggが変更されると、
eggは自動的に変更されます。
console.log(egg === newEgg); // true
残念ながら、説明した状況と同様の状況では、1つの変数に書き込まれたものが別の変数にさらされたときに変更する必要は通常ありません。 それでは、オブジェクトの突然変異を防ぐ方法は? この質問に対する答えを見つける前に、まず、JSで不変である、つまり不変であるものを見つけておくとよいでしょう。
不変のプリミティブ
JavaScriptでは、プリミティブ(
String 、
Number 、
Boolean 、
Null 、
Undefined 、および
Symbolデータ型について説明しています)は不変です。 つまり、プリミティブの構造を変更したり、プロパティやメソッドを追加したりすることはできません。 たとえば、プリミティブに新しいプロパティを追加しようとしても、まったく何も起こりません。
const egg = "Humpty Dumpty"; egg.isBroken = false; console.log(egg);
キーワードconstと免疫
constキーワードを使用して宣言された変数(定数)は不変であると多くの人が考えています。 しかし、これはそうではありません。
constキーワードを使用しても、定数に書き込まれるものは不変になりません。 定数に新しい値を割り当てることはできません。
const myName = "Zell"; myName = "Triceratops";
constキーワードを使用してオブジェクトを定義すると、その内部構造を完全に変更できます。
eggオブジェクトを使用した例では、
eggは
constキーワードを使用して作成された定数ですが、このオブジェクトは突然変異を防ぎません。
const egg = { name: "Humpty Dumpty" }; egg.isBroken = false; console.log(egg); // { // name: "Humpty Dumpty", // isBroken: false // }
オブジェクト突然変異防止
オブジェクトの突然変異を防ぐために、オブジェクトを操作するときに
Object.assignメソッドを使用できます
Object.assignメソッドは、既存のオブジェクトを組み合わせてプロパティを結果オブジェクトに割り当てることにより、新しいオブジェクトを作成する操作を実装します。
▍Object.assignメソッド
Object.assignコンストラクトを使用
Object.assignと、2つのオブジェクト(または複数のオブジェクト)を組み合わせて、1つの新しいオブジェクトを出力できます。 次のように使用できます。
const newObject = Object.assign(object1, object2, object3, object4);
newObjectは、
Object.assign渡されるすべてのオブジェクトのプロパティが含まれます。
const papayaBlender = { canBlendPapaya: true }; const mangoBlender = { canBlendMango: true }; const fruitBlender = Object.assign(papayaBlender, mangoBlender); console.log(fruitBlender);
競合する2つのプロパティが見つかった場合、
Object.assignの引数リストの右側にあるオブジェクトのプロパティは、左側のリストにあるオブジェクトのプロパティを上書きします。
const smallCupWithEar = { volume: 300, hasEar: true }; const largeCup = { volume: 500 }; // volume , 300 500 const myIdealCup = Object.assign(smallCupWithEar, largeCup); console.log(myIdealCup); // { // volume: 500, // hasEar: true // }
ただし、注意してください!
Object.assignを使用して2つのオブジェクトを結合すると、引数リストの最初のオブジェクトが変更されます。 他の人はしません。
console.log(smallCupWithEar); // { // volume: 500, // hasEar: true // } console.log(largeCup); // { // volume: 500 // }
ObjectObject.assignを使用する際の突然変異の問題の解決策
最初の
Object.assignとして、既存のオブジェクトの突然変異を防ぐために新しいオブジェクトを渡すことができます。 ただし、最初のオブジェクト(空の)はまだ変更中ですが、突然変異は重要なものには影響しないため、心配する必要はありません。
const smallCupWithEar = { volume: 300, hasEar: true }; const largeCup = { volume: 500 };
この操作を実行した後、必要に応じて新しいオブジェクトを変更できます。 これは以前のオブジェクトには影響しません。
myIdealCup.picture = "Mickey Mouse"; console.log(myIdealCup); // { // volume: 500, // hasEar: true, // picture: "Mickey Mouse" // } // smallCupWithEar console.log(smallCupWithEar); // { volume: 300, hasEar: true } // largeCup console.log(largeCup); // { volume: 500 }
▍Object.assignおよびプロパティオブジェクトへのリンク
Object.assign 1つの問題は、浅いマージを実行することです。つまり、あるオブジェクトから別のオブジェクトにプロパティを直接コピーします。 同時に、処理されたオブジェクトのプロパティであるオブジェクトへのリンクもコピーします。
これを例として考えてください。
新しいサウンドシステムを購入したとします。 パワーを制御し、音量、低音レベル、その他のパラメーターを設定できます。 これが標準のシステム構成です。
const defaultSettings = { power: true, soundSettings: { volume: 50, bass: 20,
友人の中には大音量の音楽が好きな人もいるので、家全体が耳に届くことが保証されたプリセットを作成することにしました。
const loudPreset = { soundSettings: { volume: 100 } };
次に、友達をパーティーに招待します。 システムを稼働状態にし、同時に標準設定と音量を最大にする設定の両方を使用するには、
defaultSettingsと
loudPresetを組み合わせようとします。
const partyPreset = Object.assign({}, defaultSettings, loudPreset);
ただし、音楽をオンにすると、
partyPresetシステムの音がおかしいことが
partyPresetます。 ボリュームは良好ですが、低音はまったくありません。
partyPresetを探索する
partyPreset 、低音設定がないことに驚かれます!
console.log(partyPreset); // { // power: true, // soundSettings: { // volume: 100 // } // }
これは、JavaScriptが参照によって
soundSettingsプロパティ
soundSettingsコピーするためです。
defaultSettingsと
loudPreset両方に
soundSettingsオブジェクトがあるため、
Object.assign引数の右側にあるオブジェクトが新しいオブジェクトにコピーされます。
partyPresetを変更
partyPreset 、それに応じて
partyPresetが変化します
soundSettingsから
loudPresetへのリンクがコピーされた証拠として。
partyPreset.soundSettings.bass = 50; console.log(loudPreset); // { // soundSettings: { // volume: 100, // bass: 50 // } // }
Object.assignはオブジェクトのサーフェスマージを実行するため、同様の状況で、新しいオブジェクトがプロパティオブジェクトを含むオブジェクトの組み合わせである場合、他のものを使用する必要があります。 なに? たとえば、
assignmentライブラリ。
▍ライブラリの割り当て
Assignmentは、
Pony Foo (JSに関する貴重な情報源)の
Nicolas Bevacuaによって作成された小さなライブラリです。 オブジェクトのディープマージ(ディープマージ)を実行し、同時に突然変異を心配しないことが役立ちます。
assignment使用は、異なるメソッド名を使用することを除いて、
Object.assignでの作業と同じように見えます。
// assignment const partyPreset = assignment({}, defaultSettings, loudPreset); console.log(partyPreset); // { // power: true, // soundSettings: { // volume: 100, // bass: 20 // } // }
ライブラリは、他のオブジェクトに埋め込まれたすべてのオブジェクトの値を新しいオブジェクトにコピーし、既存のオブジェクトを変更から保護します。
partyPreset.soundSettingsプロパティを変更しようとしても、
loudPresetは変更されないことがわかります。
partyPreset.soundSettings.bass = 50; // loudPreset console.log(loudPreset); // { // soundSettings { // volume: 100 // } // }
assignmentライブラリは、オブジェクトを深くマージできる多くのツールの1つにすぎません。
lodash.assignや
merge-optionsなどの他のライブラリもこれに役立ちます。 一番好きなものを安全に選択できます。
Object.assignではなく、ディープマージを常に使用する必要がありますか?
これでオブジェクトを突然変異から保護する方法がわかったので、
Object.assignを有意義に使用
Object.assignます。 この標準的な方法を正しく使用する方法を知っていれば、何も問題はありません。
ただし、ネストされたプロパティを持つオブジェクトを使用する必要がある場合は、常に
Object.assignではなくオブジェクトのディープマージを使用してください。
オブジェクトの耐性を確保する
上記で説明した方法は、オブジェクトを突然変異から保護するのに役立ちますが、それらの方法で作成されたオブジェクトの耐性を保証するものではありません。 入れ子になったオブジェクトプロパティを持つオブジェクトを
Object.assignときに間違えて
Object.assignを使用すると、後で深刻な問題が発生する可能性があります。
これから身を守るためには、オブジェクトがまったく変化しないことを保証する価値があります。
これにはImmutableJSのようなライブラリを使用できます。 このライブラリは、ヘルプを使用して処理されたオブジェクトを変更しようとするとエラーをスローします。
または、
Object.freezeメソッドと
deep-freezeライブラリを使用できます。 これらの2つのツールはエラーを生成しませんが、オブジェクトの変更も許可しません。
Object.freezeメソッドとディープフリーズライブラリ
Object.freezeメソッドは、オブジェクト自体のプロパティを変更から保護します。
const egg = { name: "Humpty Dumpty", isBroken: false }; // "" egg Object.freeze(egg); // egg.isBroken = true; console.log(egg); // { name: "Humpty Dumpty", isBroken: false }
ただし、
defaultSettings.soundSettings.baseなどの「凍結」オブジェクトのプロパティであるオブジェクトを変更しようとすると、このメソッドは役に立ちません。
const defaultSettings = { power: true, soundSettings: { volume: 50, bass: 20 } }; Object.freeze(defaultSettings); defaultSettings.soundSettings.bass = 100; // soundSettings console.log(defaultSettings); // { // power: true, // soundSettings: { // volume: 50, // bass: 100 // } // }
プロパティオブジェクトの突然変異を防ぐために、オブジェクトである「フリーズ」オブジェクトのすべてのプロパティに対して
Object.freezeを再帰的に呼び出す
ディープフリーズライブラリを使用できます。
const defaultSettings = { power: true, soundSettings: { volume: 50, bass: 20 } }; // " " ( deep-freeze) deepFreeze(defaultSettings); // , defaultSettings.soundSettings.bass = 100; // soundSettings console.log(defaultSettings); // { // power: true, // soundSettings: { // volume: 50, // bass: 20 // } // }
値と突然変異の書き換えについて
変数のエントリと、新しい値のオブジェクトのプロパティのエントリを突然変異と混同しないでください。
実際、新しい値が変数に書き込まれると、それが指すものが変わります。 次の例では、
aの値が
11から
100変更されます。
let a = 11; a = 100;
突然変異により、オブジェクト自体が変化します。 変数または定数に書き込まれたオブジェクトへの参照は同じままです。
const egg = { name: "Humpty Dumpty" }; egg.isBroken = false;
まとめ
突然変異は、コードを混乱させる可能性があり、これを行うために、まったく気付かないうちに予測できないため、危険です。 問題の原因が突然変異であると疑われる場合でも、問題のある場所を見つけることは依然として困難です。 したがって、不愉快な驚きからコードを保護する最良の方法は、オブジェクトの作成時から、突然変異からの保護を確実にすることです。
オブジェクトを突然変異から保護するには、
ImmutableJSや
Mori.jsなどのライブラリを使用するか、標準のJSメソッド
Object.assignおよび
Object.freezeを使用できます。
Object.assign Object.freezeと
Object.freezeは、オブジェクトのネイティブプロパティのみを変更から保護できることに注意してください。 オブジェクトそのものである突然変異やプロパティから保護する必要がある場合は、
割り当てや
ディープフリーズなどのライブラリが必要になります。
親愛なる読者! オブジェクトの突然変異によってJSアプリケーションで予期しないエラーが発生しましたか?
