深いコピーと浅いコピーを実現するjsのいくつかの方法と例を説明します


深いコピーと浅いコピーがあるのはなぜですか?


JSには基本型参照型があり、
基本型は文字列数値ブール値未定義null記号
参照型オブジェクト関数配列はスタックメモリヒープメモリ
を必要とします。

基本型で作成した変数や値はスタックメモリ、name、valに格納されますが、コピー時にはスタックメモリに新しいメモリが開かれ、相互に影響を与えることはありません

参照型の変数はスタック メモリ (名前) にあり、val はヒープ アドレスを指し、値はヒープ メモリに格納されます。コピーすると、スタック メモリに新しい変数が作成されますが、val は依然としてヒープ アドレスを指します。そのヒープアドレス、および値が変更されると、ヒープメモリ内の値が変更され、両方の変数の値が変更されます。

浅いコピー:
基本データ型、 slice()、concat()、Object.assign()、es6 拡張演算子「...」はすべて浅いコピーであり、最初の層ではすべて深いコピーですが、配列がネストされている、つまり 2 番目以上のレイヤーが変更された場合、データもそれに応じて変更されます。

1 層のディープ コピーを実現する:
例 1、Object.assign()

 var obj1 = {
    a: 1,
    b: 2,
    c: ['a','b','c']
}
var obj2 = Object.assign({}, obj1);
obj2.c[1] = 5;
console.log(obj1.c); // ["a", 5, "c"]
console.log(obj2.c); // ["a", 5, "c"]

例 2、スライスの実装

// 对有多层属性的数组对象使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4

例 3、concat() メソッド

 var a=[1,2,[3,4]]
 var c=[];
 var b=c.concat(a);
 b[0]=5;
 b[2][0]=6;
 console.log(b[0]);//5
 console.log(a[0])//1
 console.log(b[2][0]);//6
 console.log(a[2][0])//6

例 4、ES6 のスプレッド演算子「...」

 var a=[1,2,[3,4]]
 var b=[...a];
 b[0]=5;
 b[2][0]=6
 console.log(b[0]);//5
 console.log(a[0])//1
 console.log(b[2][0]);//6
 console.log(a[2][0])//6

例 5. Object.create()

function deepCopy(obj) {
  var copy = Object.create(Object.getPrototypeOf(obj));
  var propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    var desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
var obj2 = deepCopy(obj1);
obj2.a = 20;
obj2.b.bc = 60;
console.log(obj1.a)//1
console.log(obj2.a)//20
console.log(obj1.b.bc)//60
console.log(obj2.b.bc)//60

ディープコピー:

1. JSON.stringify および JSON.parse 2. 再帰的にネストされたコピー 3. lodash.cloneDeep

例 1、JSON.stringify および JSON.parse のラフなディープ コピー (オブジェクトのコンストラクターを放棄)

JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;
function deepCopy(obj1){
    let _obj = JSON.stringify(obj1);
    let obj2 = JSON.parse(_obj);
    return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 1,2,2,3,4

注意点:
缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,
也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;
let obj1 = {
   fun:function(){
      alert(123);
   }
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined


例 2、再帰コピー、循環参照の問題を解決する

实现原理:
使用递归的方式实现数组、对象的深拷贝。
先判断各个字段类型,然后用递归解决嵌套数据。
判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝。   
进行深拷贝的不能为空,并且是对象或者是数组。
function deepClone(obj){
  let objClone =  Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === 'object') {
    for(let key in obj){
      if (obj[key] && typeof obj[key] === 'object'){ // 判断对象的这条属性是否为对象
        objClone[key] = deepClone(obj[key]); // 若是对象进行嵌套调用
      }else{
        objClone[key] = obj[key]
      }
    }
  }
  return objClone; //返回深度克隆后的对象
}
let arrayA = [{a: 1, b: 2, aa: [{ab: 1, ac: [{ac: 1}]}]}];
let arrayB = deepClone(arrayA);
arrayB[0]['aa'][0].ab = 2;
console.log(arrayA); // [{a: 1, b: 2, aa: [{ab: 1, ac: [{ac: 1}]}]}]
console.log(arrayB); // [{a: 1, b: 2, aa: [{ab: 2, ac: [{ac: 1}]}]}]

例 3、ES プラグイン lodash

import lodash from 'lodash'

var objects = [1,{ 'a': 1 }, { 'b': 2 }]; 
var deep = lodash.cloneDeep(objects);
deep[0] = 2;
deep[1].a = 2;
console.log(objects[0]);//1
console.log(deep[0]);//2
console.log(objects[1].a);//1
console.log(objects[1].a);//2

参考記事:ディープコピーを実現するjsのいくつかの方法:https://www.jianshu.com/p/3dbe82d2826a

おすすめ

転載: blog.csdn.net/qq_43148113/article/details/124444660