[자바스크립트] 깊은(Deep Copy) 복사 얕은(Shallow Copy) 복사(★★★)

bunny.log·2022년 9월 20일
0
post-custom-banner

자바스크립트에서 값은 원시값과 참조값으로 나뉜다.

원시값

Number
String
Boolean
Null
Undefined

const a = 1;
let b = a;

b = 2

console.log(a); //1
console.log(b); //2

원시값은 값을 복사 할 때 복사된 값을 다른 메모리에 할당 하기 때문에 원래의 값과 복사된 값이 서로에게 영향을 미치지 않는다

참조값

Object
Symbol
원시값 외 모든 값들

const a = {number: 1};
let b = a;

b.number = 2

console.log(a); // {number: 2}
console.log(b); // {number: 2}

하지만 참조값은 변수가 객체의 주소를 가리키는 값이기 때문에 복사된 값(주소)이 같은 값을 가리킨다.
이런한 객체의 특징 때문에 객체를 복사하는 방법은 크게 두가지로 나뉜다.

얕은 복사 Shllow Copy

얕은 복사란 객체를 복사할 때 위의 예제처럼 원래값과 복사된 값이 같은 참조를 가리키고있는 것을 말한다. 객체안에 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은 복사라고 한다.

다음은 얕은 복사를 하는 방법이다.

const obj1 = { a: 1, b: 2};
const obj2 = obj1;
console.log( obj1 === obj2 ); // true

Object.assign()

Object.assign은 첫번째 요소로 들어온 객체에 다음인자로 들어온 객체를 복사해준다.

const obj1 = { a:1, b:2 };
const obj2 = Object.assign({}, obj1);
obj2.a = 100;
console.log( obj1 === obj2 ) // false
console.log( obj1.a ) // 1

Object.assign() 메소드를 통해 첫 번째 인자로 빈 { } 객체를, 두 번째 인자로 obj1 넣고 obj2 에 할당하였다.
이제 obj1과 obj2는 다른 주소를 갖게되었다. (그러나 딱, 1 depth 까지만)

전개연산자

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = {...obj}

copiedObj.b.c = 3

obj === copiedObj // false
obj.b.c === copiedObj.b.c // true

깊은 복사 Deep Copy

깊은 복사된 객체는 객체안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.
얇은 복사 처럼 주소를 복사해서 공유하는 것이 아니라, 아예 새로운 객체안 속성(property)만 복사 해서 사용할 수 없을까?

…(spread) 연산자를 통한 복사 (과연 깊은 복사가 될까?)

const obj1 = { a:1, b:2 };
const obj2 = { ...obj };
obj2.a = 100;
console.log( obj1 === obj2 ) // false
console.log( obj1.a ) // 1

...(spread) 연산자를 통해 { }안에 obj1의 속성을 복사하여 obj2에 할당하였다.
이제 obj1과 obj2는 다른 주소를 갖게되었다. (그러나 딱, 1 depth 까지만)

  1. 재귀함수를 이용한 복사
const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

function copyObj(obj) {
  const result = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      result[key] = copyObj(obj[key]);
    } else {
      result[key] = obj[key];
    }
  }

  return result;
}

const copiedObj = copyObj(obj);

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false 

JSON.stringify()

JSON.stringify()는 객체를 json 문자열로 변환하는데 이과정에서 원본 객체와의 참조가 모두 끊어진다. 객체를 json 문자열로 변환후 JSON.parse()를 이용해 다시 자바스크립트 객체로 만들어주면 깊은 복사가 된다.

하지만 이방법은 사용하기는 쉽지만 다른 방법에비해 아주 느리다고 알려져있다.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = JSON.parse(JSON.stringify(obj));

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false 

lodash 라이브러리 사용

lodash 라이브러리를 사용하면 깊은 복사를 더 쉽게 할 수 있다.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const copiedObj = _.cloneDeep(obj);

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false

이중 배열 복사 시

case 1

let arr = [
        [0, 0, 1, 0, 0],
        [0, 0, 1, 0, 1],
        [0, 1, 1, 0, 1],
        [0, 0, 0, 0, 1],
      ];

      function solution(arr) {
        var arr2 = arr.slice();

        for (let i = 0; i < arr2.length; i++) {
          arr2[i] = arr2[i].map((v) => v + 1);
        }

        console.log("arr1: ", arr);
        console.log("arr2: ", arr2);
      }

      solution(arr);

//arr1
//[0, 0, 1, 0, 0]
//[0, 0, 1, 0, 1] 
//[0, 1, 1, 0, 1]
//[0, 0, 0, 0, 1]

//arr2
//[1, 1, 2, 1, 1]
//[1, 1, 2, 1, 2]
//[1, 2, 2, 1, 2]
//[1, 1, 1, 1, 2]

case 2

let arr1 = [
  [1, 2, 3],
  [2, 3, 4],
];

let arr2 = arr1.map((v) => [...v]);

arr2[0][0] = 0;

console.log(arr1); // [[1,2,3],[2,3,4]]
console.log(arr2); // [[0,2,3],[2,3,4]]

case 3

const arr = [[0, 0], [0, 0], [0, 0]];

const copy = arr.map(v => v.slice());

copy[1][1] = 1;
console.log(copy); // [[0, 0], [0, 1], [0, 0]]
console.log(arr); // [[0, 0], [0, 0], [0, 0]]
profile
https://github.com/nam-yeun-hwa
post-custom-banner

0개의 댓글