[JS]深放射&浅放射



概要


前述のバージョンJS、オリジナルタイプ&オブジェクトで説明したように、JSは複数のオブジェクトをサポートしており、少し複雑なデータ構造が形成されている場合、配列またはオブジェクトにデータを格納する作業が多い.
「レプリケーション」の概念は、このような観点から、単純な元のタイプについては、通常考えられるレプリケーションかもしれませんが、オブジェクトレベルからレプリケーションについて深く考える必要があります.

本題


その前に確認する
値のコピー
  • 変数はすべてメモリに格納されます.
  • 値レプリケーションは、それぞれ独立したメモリアドレスを有します.
  • ですので、コピー後、操作があってもデータごとに情報が損なわれることはありません.
  • リファレンスのコピー(メモリアドレス)
  • の値は1つですが、変数は複数です.
  • これを参照とします.
  • JSでは、番号、String、Booleanなどの元のタイプを除いて、すべてのオブジェクトがメモリアドレスにコピーされます.
  • これは、同じ値に対して複数の名前(識別子)にコピーされる概念が生成されるが、実際には1つの値に対して同じメモリのアドレス値があることを意味する.
  • のため、今後のデータ操作には予想外のホットスポットが現れる.
  • 📍 問題の状況を示す

    {
      /**
       * 값 복사
       * - 분리된 메모리 공간에, 데이터를 각각 저장(공유 X) 💡
       */
      let num1 = 1;
      let num2 = num1;
    
      num2 = 2;
      console.log(`num1 : ${num1}, num2 : ${num2}`);
      // num1 : 1, num2 : 2
      // 🔍 원본 데이터 값 보존
    
      /**
       * 객체 복사
       * - 동일한 메모리 공간 내, 주소값 데이터에 대해, 동일한 주소값 데이터를 각각 저장(공유 O) 💡
       */
      let person = {
        name: "min",
        job: "developer",
        salary: 5000,
      };
      let clonePerson = person;
      clonePerson.salary = 6000;
    
      console.log(`Salary - Person : ${person.salary}, ClonePerson : ${clonePerson.salary}`);
      // Salary - Person : 6000, ClonePerson : 6000
      // 🔍 원본 데이터 값 변경됨
    }

    📍 複製参照タイプ-浅い複製(Shallow Copy)


    参照タイプのコピーは、基本的に浅いコピーと深いコピーに分けられます.ここでは、まず浅いコピーについて説明します.
    上記の場合、元のタイプではなく通常のオブジェクトの単純な代入レプリケーションによる問題を解決し、そのオブジェクトのすべての内部データが元のタイプ値で構成されている場合は、Shallow Copyを使用して完全に解決できます.
    JSでの浅いコピーの方法はいくつかありますが、ES 6+以降の標準では、通常と比較的簡単な方法は:
  • Object.assgin()
  • の使用
    /**
       * Shallow Copy - Object.assign() 사용
       */
      let myCat = {
        name: "Cash",
        age: 5,
        weight: "3kg",
      };
      let myCat2 = Object.assign({}, myCat);
      myCat2.name = "Rolly";
      myCat2.age = 2;
    
      console.log(myCat); // { name: 'Cash', age: 5, weight: '3kg' }
      console.log(myCat2); // { name: 'Rolly', age: 2, weight: '3kg' }
  • Spread Operator
  • を使用
     /**
       * Shallow Copy - Spread Operator 사용
       */
      let myCaptin = {
        name: "Iron Man",
        rank: 1,
        gender: "M",
      };
      let myCaptin2 = { ...myCaptin };
      myCaptin2.name = "Captin America";
      myCaptin2.rank = 2;
    
      console.log(myCaptin); // { name: 'Iron Man', rank: 1, gender: 'M' }
      console.log(myCaptin2); // { name: 'Captin America', rank: 2, gender: 'M' }
    💡 [注意]
    アレイ(Array)は、オブジェクト(Spread OperatorまたはArray Object)ではなく、埋め込み方法です.prototyp.slice( ) , Array.prototype.map()などの方法も既存のアレイデータを操作して新しいアレイ自体の概念を返すため,オブジェクト上で概念と同じShallow Copyを実行することができる.
    この2つの方法から、データは、最初のレプリケーションオブジェクトと比較して、レプリケーション値などの変数ごとに保持されているようです.
    さっき話した時~いいみたい.こう言うのは理由がある.
    次の状況を見てください.
     /**
       * Shallow Copy 에 함정
       */
      let myCar = {
        name: "부릉이",
        price: 7000,
        specialMode: {
          mode: "Fly",
        },
      };
      let myCar2 = { ...myCar };
      myCar2.name = "따릉이";
      myCar2.price = 4000;
      myCar2.specialMode.mode = "Dive";
    
      console.log(myCar); // { name: '부릉이', price: 7000, specialMode: { mode: 'Dive' } }
      console.log(myCar2); // { name: '따릉이', price: 4000, specialMode: { mode: 'Dive' } } 🔍
    
    // 두 car에 대해 프로퍼티 비교
      console.log(myCar.name === myCar2.name); // false
      console.log(myCar.specialMode === myCar2.specialMode); // true 🔍
      console.log(myCar.specialMode.mode === myCar2.specialMode.mode); // true 🔍
    いずれもよく、最後に、SpecialMode(オブジェクト)Propertyの内部には、モードPropertyがShallow Copyを行いmyCar 2でSpecialModeを変更したにもかかわらず、myCarとmyCar 2の2つのcarがモードを「Dive」モードに変更していることがわかります.
    どうしたの?
    これがShallow Copyを使用する際の注意点です.
    MDNでSpread Operatorドキュメントを表示するときにこのような説明があります.

    本書は、Array CopyでSpread Operatorを使用した場合の説明です.以下、Objectです.assign()も同じ概念と言います.
    重要なのは、「深さ」(Depth)は1であり、簡単に言えば、上記の例ではmyCarオブジェクトの内部プロファイルについてDepthにたとえている.

    より容易に理解できるように、最外層のコピーのみをコピーすることは、浅いコピーとみなすことができる.
    このように見ることができますが、もっと詳しく理解するには

  • Shallow Copyは、元のオブジェクトのプロパティ値を新しいオブジェクトに正確にコピーします.

  • ただし、約propertyの値が元のタイプではなく、「オブジェクト」の場合、オブジェクトのアドレスがコピーされます.🔍

  • すなわち、コピーされたオブジェクトは元のオブジェクトと同じpropertyと値を有するが、アドレスコピーされたpropertyは新しい形式ではなく「同じ」(オブジェクトのメモリアドレス)を共有する.
  • したがって、最初の一般オブジェクトの持ち込みレプリケーションと同様に、オブジェクト内部の別の「オブジェクト」または「関数」プログラムが同じデータのメモリアドレスを共有するため、完全なレプリケーションは実現できません.

    📍 参照タイプのコピー-深度コピー


    深度コピーは、元のオブジェクトを完全にコピーします.
    さっき見たように、Shallow Copyは致命的なトラップではなく、新しいメモリスペースを増やすことで作成されます.
    たとえば、先ほどのShallow Copyの場合と比較して、Deep Copyは次のような内部構造を持っています.
    Propertyにオブジェクトやメソッドなどの追加のDepthがある場合でも、完全に独立した新しいメモリ領域を得ることができます.

    深度コピーを実現する方法は、次のとおりです.
    1️⃣. JSON.parse()とJSON.stringgify()関数の使用
  • JSON.stringify関数を使用してオブジェクト全体を文字列に変換し、
  • 再びJSON.parse関数を使用して文字列
  • をオブジェクト形式で返す
  • により、文字列に変換された瞬間に参照値が中断されるので、「新規」オブジェクトとして作成して使用できます.🔍
  • ですが、JSON関数は大量のリソースを消費する関数です.性能の悪い部分を考えるべきです.
  •  /**
       * Deep Copy(깊은 복사) - JSON 함수를 이용
       */
      let myFruit = {
        name: "Apple",
        price: 1000,
        characteristic: {
          favor: "Sweet",
        },
      };
      let myFruit2 = JSON.parse(JSON.stringify(myFruit));
      myFruit2.characteristic.favor = "Very Sweet";
    
      console.log(myFruit); // { name: 'Apple', price: 1000, characteristic: { favor: 'Sweet' } }
      console.log(myFruit2); // { name: 'Apple', price: 1000, characteristic: { favor: 'Very Sweet' } } 🔍
    最後に,myFruitとmyFruti 2は完全に分離して用いられているが,特徴(オブジェクトProperty)PropertyのDepthは1ではない.
    前述のJSON関数を使用する場合、次のような問題が発生します.
  • JSONデータには関数データ型がないため、関数属性が失われます.
  • /**
       * Deep Copy - JSON 함수 사용시 function 누락 현상
       */
      let myObj = {
        name: "obj",
        sayHi: function () {
          console.log("Hello Javascript");
        },
      };
      let copyMyObj = JSON.parse(JSON.stringify(myObj));
      console.log(myObj.sayHi); // [Function: sayHi]
      console.log(copyMyObj.sayHi); // undefined 🔍
    このほか、オブジェクトツリーにループ参照がある場合、stringpyメソッドでループ構造をJSONに変換するタイプエラーが発生します.2️⃣. LodashのCloneDeep関数の使用
  • LodashはJS高次関数と関数型ライブラリです.
  • .cloneDeep(value)関数を使用します.
  • /**
       * Deep Copy(깊은 복사) - lodash 에 cloneDeep 함수를 이용
       * - lodash 라이브러 import 필요
       */
    
      const _ = require("lodash");
    	
      let myFruit = {
        name: "Apple",
        price: 1000,
        characteristic: {
          favor: "Sweet",
        },
        sayHi: function () {
          console.log("과일이 인사를 하네요.");
        },
      };
    
      let myFruit2 = _.cloneDeep(myFruit);
      console.log(myFruit);
      // {
      // 	name: 'Apple',
      // 	price: 1000,
      // 	characteristic: { favor: 'Sweet' },
      // 	sayHi: [Function: sayHi]
      // }
    
      console.log(myFruit2);
      // {
      // 	name: 'Apple',
      // 	price: 1000,
      // 	characteristic: { favor: 'Sweet' },
      // 	sayHi: [Function: sayHi]
      // }
    3️⃣. ダイレクトインプリメンテーション
  • は、オブジェクトツリーに沿ってエンドノードに再帰的に関数をコピーする必要がある.
  • 以上のpostingから見ると、大部分は以下の方法で実現されている.
  •   /**
       * Deep Copy - 재귀를 이용한 직접 구현
       */
    
      function clone(source) {
        var target = {};
        for (let i in source) {
          if (source[i] != null && typeof source[i] === "object") {
            target[i] = clone(source[i]); // resursion
          } else {
            target[i] = source[i];
          }
        }
        return target;
      }
      let myFruit = {
        name: "Apple",
        price: 1000,
        characteristic: {
          favor: "Sweet",
        },
        sayHi: function () {
          console.log("과일이 인사를 하네요.");
        },
      };
      const myFruit2 = clone(myFruit);
    
      console.log(myFruit); 
      // {
      // 	name: 'Apple',
      // 	price: 1000,
      // 	characteristic: { favor: 'Sweet' },
      // 	sayHi: [Function: sayHi]
      // }
    
      console.log(myFruit2); 
      // {
      // 	name: 'Apple',
      // 	price: 1000,
      // 	characteristic: { favor: 'Sweet' },
      // 	sayHi: [Function: sayHi]
      // }

    n/a.結論


    [TMI]
    結局、今までJSは「コピー」を間違って知っていました(私のように)😁) 多くの人がDepthが1のデータだけをShallow Copyとしているので、「ん~異常なし」と考えているのであれば、本当に重要なのはDeep Copyです.
    それは知る必要があるようです.
    オブジェクトをコピーする場合、「Shallow Copyは良くない」と無条件に判断するのは間違いです.場合によっては、Shallow Copyだけでオブジェクトをコピーすることができ、Deep Copyでデータを注意深くコピーする必要がある場合があります.
    それを正しく判断して使うことが大切です.
    このテーマを調べたところ、以下のようにコピーが重要だと思う理由が書かれていました.
    予知できないエラーを最小限に抑える.
    同意します.JS自体は,実行時言語であるため,途中で思いがけない場所でデータが操作される可能性があり,デバッグ時にプログラムが大きくなるほど発見が困難になる.当初は今回まとめたShallow CopyやDeep Copyを考えた方が良いと思います.
    反復可能なオブジェクトについて説明します.

    リファレンス

  • Yotube
  • zero choブログ
  • Dalesoのブログ
  • https://roseline.oopy.io/dev/javascript-back-to-the-basic/shallow-copy-deep-copy
  • https://mygumi.tistory.com/322
  • https://soldonii.tistory.com/138
  • https://helloinyong.tistory.com/267