JavaScript基礎心法——深さコピー


原文の住所:JavaScript基礎心法——深さコピー
スターを歓迎します
間違ったところがあれば、ご指摘ください.
浅いコピーの和深コピーはすべてJSの中の引用のタイプにとって、浅いコピーはコピーの対象の引用だけで、コピーした後の対象が変化すると元の対象も変化します.深くコピーするだけが対象に対するコピーです.
前言
深浅コピーというと、先に言及しなければならないのはJavaScriptのデータのタイプで、前の文章はJavaScriptの基礎心法です.データのタイプははっきり言っています.ここでは多くは言いません.
知っておくべきなのは、JavaScriptのデータタイプは基本データタイプと参照データタイプに分けられています.
基本的なデータタイプのコピーについては、深くて浅いコピーという区別はありません.浅いコピーはすべて参照データタイプにとってです.
浅いコピー
浅いコピーとは、引用だけをコピーして本物の値をコピーしないという意味です.
const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};

const cloneArray = originArray;
const cloneObj = originObj;

console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}

cloneArray.push(6);
cloneObj.a = {aa:'aa'};

console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]

console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
上のコードは、最も単純な利用=の値演算子によって、浅いコピーが実現され、cloneArrayおよびcloneObjによって変更され、originArrayおよびoriginObjも変化していることが分かります.
コピー
深くコピーするということは目標に対する完全コピーで、浅いコピーのように引用をコピーしただけで、値までコピーしました.
深くコピーしたら、いつまでも付き合いがなく、誰にも影響がありません.
現在、深くコピーする方法は多くないです.主に二つです.
  • は、JSONオブジェクトのparseおよびstringify
  • を利用する.
  • は、再帰的に各レイヤがオブジェクトを再作成し、値を付与するために利用される
  • .
    JSON.strigify/parseの方法
    まずこの二つの方法を見てみましょう.
    The JSON.strigify()method converts a JavaScript value to a JSON string.JSON.stringifyは、一つのJavaScript値を一つのJSON文字列に変換するものである.
    The JSON.parse()method parses a JSON string、construting the JavaScript value or object described by the string.JSON.parseは、一つのJSON文字列を一つのJavaScript値またはオブジェクトに変換するものである.
    分かりやすいでしょう.JavaScript値とJSON文字列の相互変換です.
    深度コピーが可能ですか?試してみます
    const originArray = [1,2,3,4,5];
    const cloneArray = JSON.parse(JSON.stringify(originArray));
    console.log(cloneArray === originArray); // false
    
    const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
    const cloneObj = JSON.parse(JSON.stringify(originObj));
    console.log(cloneObj === originObj); // false
    
    cloneObj.a = 'aa';
    cloneObj.c = [1,1,1];
    cloneObj.d.dd = 'doubled';
    
    console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
    console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
    確かに深いコピーです.便利です.しかし,この方法はいくつかの簡単な場合にのみ適用できる.例えば次のようなオブジェクトは適用されません.
    const originObj = {
      name:'axuebin',
      sayHello:function(){
        console.log('Hello World');
      }
    }
    console.log(originObj); // {name: "axuebin", sayHello: ƒ}
    const cloneObj = JSON.parse(JSON.stringify(originObj));
    console.log(cloneObj); // {name: "axuebin"}
    cloneObjに属性が失われていることが分かりました.それはなぜですか?MDNに原因が見つかった.
    If undefined,a function,or a smmbol is encountered duversion itiseether maitted(when ititisfound in found in Oneject)or censored to null(when itisfound in array).JSOSOSON.ststininininininininiican can alsosososososojjjjjuuuuuuffffjjjjjjjjuuuuffffftjuneeeeeedededededededededededededededededededededededededededededededededededededededededededededened)undefinedfunctionsymbolは、変換中に無視される.
    わかりました.つまり、対象に関数が含まれている場合は、この方法でディープコピーはできません.
    再帰的方法
    再帰的な思想は簡単です.つまり、各階層のデータに対して一回 -> の操作を実現します.簡単にコードを乱暴にします.
    function deepClone(source){
      const targetObj = source.constructor === Array ? [] : {}; //               
      for(let keys in source){ //     
        if(source.hasOwnProperty(keys)){
          if(source[keys] && typeof source[keys] === 'object'){ //       ,     
            targetObj[keys] = source[keys].constructor === Array ? [] : {};
            targetObj[keys] = deepClone(source[keys]);
          }else{ //     ,     
            targetObj[keys] = source[keys];
          }
        } 
      }
      return targetObj;
    }
    試してみます
    const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
    const cloneObj = deepClone(originObj);
    console.log(cloneObj === originObj); // false
    
    cloneObj.a = 'aa';
    cloneObj.c = [1,1,1];
    cloneObj.d.dd = 'doubled';
    
    console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
    console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
    いいです.関数付きのを試してみます.
    const originObj = {
      name:'axuebin',
      sayHello:function(){
        console.log('Hello World');
      }
    }
    console.log(originObj); // {name: "axuebin", sayHello: ƒ}
    const cloneObj = deepClone(originObj);
    console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}
    いいです.片付く
    これで終わりだと思いますか?もちろんです
    JavaScriptのコピー方法JavaScriptにおいて、配列には2つの方法concatsliceがあり、元の配列へのコピーが可能であり、これらの2つの方法はいずれも元の配列を修正することなく、修正された新しい配列を返すことができることを知っている.
    一方、ES 6にObject.assgn方法と...展開演算子を導入しても、オブジェクトのコピーを実現することができる.
    それらは薄いコピーですか?それとも深いコピーですか?
    cat
    The concat()method is used to merge two or more arrays.This method does not change the existing arrays,but instead returns a new array.
    この方法は、2つ以上の配列を接続することができますが、既存の配列を変更することなく、新しい配列を返します.
    この意味を見ていると、深くコピーしたようです.試してみます.
    const originArray = [1,2,3,4,5];
    const cloneArray = originArray.concat();
    
    console.log(cloneArray === originArray); // false
    cloneArray.push(6); // [1,2,3,4,5,6]
    console.log(originArray); [1,2,3,4,5];
    深くコピーしたように見えます.
    私たちは一つの問題を考えてみます.もしこの対象が重層なら、どうなりますか?
    const originArray = [1,[1,2,3],{a:1}];
    const cloneArray = originArray.concat();
    console.log(cloneArray === originArray); // false
    cloneArray[1].push(4);
    cloneArray[2].a = 2; 
    console.log(originArray); // [1,[1,2,3,4],{a:2}]
    originArrayには、配列[1,2,3]とオブジェクト{a:1}とが含まれています.配列とオブジェクトを直接修正すると、originArrayに影響はありませんが、配列[1,2,3]やオブジェクト{a:1}を変更すると、originArrayも変化していることが分かります.
    結論:concatは、配列の第1の層だけを深くコピーする.
    slice
    The slice()method returns a show copy of a potion of an array into a new array object selected from begin to end.The orial array will not be mofied.
    説明は全部直接a shallow copyと書いてあります.
    しかし、わけではない!
    const originArray = [1,2,3,4,5];
    const cloneArray = originArray.slice();
    
    console.log(cloneArray === originArray); // false
    cloneArray.push(6); // [1,2,3,4,5,6]
    console.log(originArray); [1,2,3,4,5];
    同じように、多層の配列を試してみます.
    const originArray = [1,[1,2,3],{a:1}];
    const cloneArray = originArray.slice();
    console.log(cloneArray === originArray); // false
    cloneArray[1].push(4);
    cloneArray[2].a = 2; 
    console.log(originArray); // [1,[1,2,3,4],{a:2}]
    やはり、結果はconcatと同じです.
    結論:sliceは、配列の第1の層だけを深くコピーする.
    Object.assign()
    The Object.assign()method is used to copy the values of all enumerable own properties from one or more source oject to a target oject.It will return the target object.
    コピーコピーします.
    じゃ、浅いコピーですか?それとも、深くコピーしますか?
    自分で試してみましょう.
    結論:Object.assign()コピーは属性値である.ソースオブジェクトの属性値がオブジェクトへの参照であれば、その参照値だけをコピーします.
    …演算子を展開する
    const originArray = [1,2,3,4,5,[6,7,8]];
    const originObj = {a:1,b:{bb:1}};
    
    const cloneArray = [...originArray];
    cloneArray[0] = 0;
    cloneArray[5].push(9);
    console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]
    
    const cloneObj = {...originObj};
    cloneObj.a = 2;
    cloneObj.b.bb = 2;
    console.log(originObj); // {a:1,b:{bb:2}}
    結論:...は、オブジェクトの第1層の深度コピーである.後はコピーの参照値だけです.
    最初のレイヤーのコピー
    分かりました.目的の対象の第一層を深くコピーして、後は浅いコピーで、「最初の層が浅いコピー」と呼ばれる場合があります.
    このような関数は自分で実現できます.
    function shallowClone(source) {
      const targetObj = source.constructor === Array ? [] : {}; //               
      for (let keys in source) { //     
        if (source.hasOwnProperty(keys)) {
          targetObj[keys] = source[keys];
        }
      }
      return targetObj;
    }
    テストしてみます.
    const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
    const cloneObj = shallowClone(originObj);
    console.log(cloneObj === originObj); // false
    cloneObj.a='aa';
    cloneObj.c=[1,1,1];
    cloneObj.d.dd='surprise';
    上記の修正を経て、cloneObjはもちろん、{a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}です.originObjは?先ほど私達はcloneObj === originObjfalseであることを検証しました.この二つのオブジェクトの参照アドレスが違っていると説明しました.
    console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
    console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
    What happendcloneObjにおいて、originObjoriginObjについては影響されていませんが、aのうちの一つのオブジェクトが修正されました.約束の深コピーは?引用住所は全部違っていますか?
    なるほど、
  • cのコードから分かるように、私たちは第一層の目標だけをdで行いました.第二層からの目標は直接shallowCloneで値演算子を割り当ててコピーしたものです.
  • so、二階目以降の目標はいずれも一つの引用をコピーしただけで、つまり浅いコピーです.
  • 締め括りをつける
  • 割当演算子 は、浅いコピーであり、オブジェクトの参照値のみをコピーする.
  • JavaScriptの配列とオブジェクトのコピー方法はすべて「最初の層が浅いコピー」です.
  • =は、深度コピーを実現しているが、目標オブジェクトに対して要求がある.
  • 本当に深いコピーをしたいなら、再帰してください.