[TIL]可変オブジェクトの浅い/深いコピー

29475 ワード

TILを書く前に..。


私には間違った習慣がたくさんあります(…)その中に修正できない問題がある.
毎日学んだことを整理して、ブログを書かないことです.
ブログする必要ないと思って通り過ぎてどうしたんですか.その日のメモでやるべきことをもう一度見てみると、
明らかに、深いレプリケーションにはブログが書かれています.私もメモリを整理する必要がありますか...?私の頭の中のGCはきっと過負荷で、何でも処理したと思います.ㅡ.,ㅡ ..
とにかく.今日また浅い、深い放射線についてよく整理されたブログを見つけました.発見も能力です...あるいは、誰もが探しているかもしれませんが、この文章を引用して整理することができます.私の頭の中のGCにこれを処理させないでください.
多くの人が使用する方法では、可変オブジェクトを深くコピーすることは絶対にできません.😢 いくつかの異なる方法で深度コピーができるそうですが、まず2つとも例を挙げて比較します.
次に、基本可変オブジェクトのメモリ参照方法を示します.

演算子「=」として参照を割り当てる


オブジェクトの場合


let arr = {a:1, b:2, e:{d:3}};
let copied = arr;
copied.e.d = 6; //arr.e.d = 6;
//=> 참고로 객체안의 객체속성에 할당할 시에, dot notation을 쓰지 않으면 안된다.
console.log(copied); //or console.log(arr);

//{a: 1, b: 2, e: {…}}
//a: 1
//b: 2
//e: {d: 6}

アレイの場合

let arr = [1, 2, [3, 4]];
let copied = arr;
arr[2].push(6); //copied[2].push(6);
console.log(arr); //console.log(copied);

//[1, 2, Array(3)]
//0: 1
//1: 2
//2: (3) [3, 4, 6]
オブジェクトはデフォルトでリファレンスタイプ(reference type)であるため、メモリアドレス値がコピーされます.したがって、arrとcopyは同じメモリを「使用」します.(でも「共有」という概念ではありません.なぜ...?勉強が必要...)
したがって、この場合、「===」または「====」演算子を使用する場合は、アドレスが同じかどうかを確認する必要があります.

浅い放射


'Array.slice()メソッドを使用して配列に参照を割り当てる


これはよく使われる方法です.なぜなら、()にstartIndexとendIndexがない場合、配列全体をコピーすることができるからです.
let arr = [1, 2, [3, 4]];
let copied = arr.slice(); //[1,2,[3,4]]

copied.push(5);
console.log(arr);
console.log(copied);


//(3) [1, 2, Array(2)]
//(4) [1, 2, Array(2), 5] 
//카피된 copied배열에만 푸쉬된 값이 들어간다. 
//음, 그럼 slice()쓰면 깊은복사가 되는건가? 독립적인 객체가 되는것인가??
でもこのアリーはprototype.slice()メソッドの欠点は,オーバーラップ構造をコピーできないことである.❗❗
let arr = [1, 2, [3, 4]];
let copied = arr.slice(); //[1,2,[3,4]]

copied[2].push(5);
console.log(arr);
console.log(copied);


// (3) [1, 2, Array(3)] //오잉? 원본배열도 변경이 되버린다..
// (3) [1, 2, Array(3)] 
コピーされた配列は、arrに関連付けられていないコピーされたオブジェクトですが、重複する構造を変更すると、ソースとコピーが影響を受けます.😥 アリーがprototype.slice()メソッドは浅いレプリケーションを実行するためです.したがって、すべての値を独立してコピーすることはできません.

'Object.create()メソッドを使用するオブジェクトへの参照の割り当て


まず次の例を見てみましょう.
let original = {
  a: 1,
  b: 2,
  c: {
    d: 3
  }
};

let copied = Object.create(original);
original.a = 1000;
original.c.d = 3000;

console.log(copied.a);	// 1000
console.log(copied.c.d);// 3000
//엇. 이것도 얼핏 봤을땐, copied 객체에 복사가 잘 되어 있는 것처럼 '보여진다'...

console.log(copied);
MDNでいうObject.create()メソッドの定義は、

'Object.create()メソッドは、指定したプロトタイプオブジェクトとプロパティを持つ新しいオブジェクトを作成します。


に表示されます.コピーする方法ではなく、プロトタイプオブジェクトを表示する新しいオブジェクトを作成します.元のオブジェクトのプロパティがコピーされ、コピーリファレンスに割り当てられます.
はい、コピーをコンソールにコピーします.結果は次のとおりです.
console.log(copied);		// {}

original.hasOwnProperty('a');	// true
copied.hasOwnProperty('a');	// false
コピーされたオブジェクトは「何も割り当てられていません」.したがって、元のオブジェクトのプロパティ
コピーしたオブジェクトにないことを確認できます.
すなわち,このように複製することはできないだけでなく,元のオブジェクトは複製オブジェクトのプロトタイプにすぎない.

拡張オペレータを使用した参照の割り当て



図01.ソース)深い放射線と浅い放射線の深い物語
すべてのJSのオブジェクトには、上の赤いブロックに対応する「繰り返し」動作を実行する[Symbol Identar]というpropertyが必要です.

オブジェクトの場合

let obj = {a:1, b:2, e:{d:3}};
let copied = {...obj}; //{a:1, b:2, e:{d:3}}

obj.a = 100;
console.log(obj);
console.log(copied);


//{a: 100, b: 2, e: {…}}
//{a: 1, b: 2, e: {…}}

アレイの場合

let arr = [1, 2, [3, 4]];
let copied = [ ...arr ]; //[1, 2, [3, 4]]

copied.push(5);
console.log(arr);
console.log(copied);


// (3) [1, 2, Array(2)]
// (4) [1, 2, Array(2), 5]
入りやすいように見えますが、重ね構造を変えると…
let obj = {a:1, b:2, e:{d:3}};
let objCopied = {...obj}; //{a:1, b:2, e:{d:3}}
obj.e.d = 3000;

let arr = [1, 2, [3, 4]];
let arrCopied = [ ...arr ]; //[1, 2, [3, 4]]
arrCopied[2].push(5);

console.log('obj{}객체는 변경됐을까요? a:1-> a:'+obj.e.d);
console.log('objCopied{}객체는 변경됐을까요? a:1-> a:'+objCopied.e.d);
console.log('--------------------------');
console.log('arr[]배열은 변경됐을까요? ->'+arr);
console.log('arrCopied[]배열은 변경됐을까요? ->'+arrCopied);
// obj{}객체는 변경됐을까요?       a:1-> a:3000
// objCopied{}객체는 변경됐을까요? a:1-> a:3000
// --------------------------
// arr[]배열은 변경됐을까요?       ->1,2,3,4,5
// arrCopied[]배열은 변경됐을까요? ->1,2,3,4,5
以上のように、オブジェクトは元のネスト構造に触れ、配列はコピーのネスト構造に触れ、オブジェクトと配列は元の構造とコピーの構造を変更します.深度コピーができないという意味です.
文の中では説明していませんが.
  • 繰り返し文にコピーする場合は
  • です.
  • Object.assign()を使用する場合(オブジェクトのみ)
    同様に浅い放射線となり、両方が変わったことを確認した.
  • ふかほうしゃ


    'JSON.parse' & 'JSON.「stringgify」による深さレプリケーション

    let arr = [1, 2, [3, 4]];
    let copied = JSON.parse(JSON.stringify(arr)); //[1, 2, [3, 4]];
    
    copied[2].push(5);
    console.log('arr[]배열은 변경됐을까요? ->'+arr);
    console.log('copied[]배열은 변경됐을까요? ->'+copied);
    //   arr[]배열은 변경됐을까요? ->1,2,3,4
    //copied[]배열은 변경됐을까요? ->1,2,3,4,5
    オリジナルは変更されず、それぞれ独立してメンテナンスされていることがわかります.もちろん、元のアレイを変更しても同じです.
    まずはJSONなのでStringify()を使用して、値をオブジェクトループではなくJSON文字列に変換します.Stringタイプは不変の形状を持つ元のタイプなので、オブジェクトへの参照は失われます.JSON.これはparse()を使用して変形した文字をオブジェクトに戻す方法です.
    ただし、このJSONオブジェクトは専用リストECMA-404によって管理されている.

    図2.)ECMA-404 documentation
    上図はJSON値で表すことができるタイプを示しています.したがって,それらのタイプに含まれない値はJSON値として認められない.したがって,関数やbigInt等値を変換することはできない.
    またdeep copyのコピーを元のコピーと比較するとfalse(表示方式が異なるため)が現れるのは不思議な問題です.

    「Lodash」と「Ramda」を使用した深度コピー


    「Lodash」はJSの関数ライブラリであり、「Ramda」も多くの人が利用しているライブラリの一つです.

    Lodash

    let a = require('lodash');
    let arr = {a:1, b:2, e:{d:3}};
    let deep = _.cloneDeep(arr);
    
    deep.a = 1000;
    deep.b.c = 2000;
    
    console.log(deep.a);   // 1
    console.log(deep.b.c); // 2
    .clone()関数を再帰実行の原理と呼ぶ.

    Ramda

    let a = require('Ramda');
    let arr = {a:1, b:2, e:{d:3}};
    let deep = a.clone(arr);
    
    deep.a = 1000;
    deep.b.c = 2000;
    
    console.log(deep.a);   // 1
    console.log(deep.b.c); // 2
    lodashは文法に似ている.しかしコードの駆動原理はかなり抽象的だそうです.

    n/a.結論


    深度レプリケーションでは、いくつかのライブラリでメソッドがうまく実装されており、このライブラリを使用することができます.今回underbar作業で使用したJSON.私はstringgify()の方法についてよく知らないので、今回整理するときにもっと知りました.さらに、ライブラリ内のこれらの関数は再帰的に実行されるため、関数も再帰的に記述すると、深度コピーが行われる可能性があります.
    参考文献
    1.JavaScriptディープコピートラップhttps://velog.io/@ashnamuh/Javascript-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%9D%98-%ED%95%A8%EC%A0%95
    2.JavaScriptオブジェクトのコピーhttps://junwoo45.github.io/2019-09-23-deep_clone/
    3.深い放射線と浅い放射線の深い物語https://medium.com/watcha/%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%8B%AC%EB%8F%84%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%95%BC%EA%B8%B0-2f7d797e008a
    ご指摘を歓迎します.いつでも意見を残してください.学習の過程ですありがとうございます.