정리/Question

얕은 복사 vs 깊은 복사 + 함정

kimddakki 2022. 5. 13. 23:23
원시값 Copy

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

const a = 1;
let b = a;
b = 2;

console.log(a); // 1
console.log(b); // 2
객체 (참조) Copy

참조값은 변수가 객체의 주소를 가리키는 값이 되기 때문에 복사된 값(주소)이 같은 값을 가리키게 됩니다.

const c = { number: 1 };
let d = c;

d.number = 2;

console.log(c.number); // 2
console.log(d.number); // 2

const e = [1, 2, 3];
let f = e;
f[0] = 10;

console.log(e); // [10, 2, 3]
console.log(f); // [10, 2, 3]

얕은 복사 Shllow: 얕은 copy 복사

대입 연산자를 이용하여 변수를 copy 할 때 객체의 주소 값을 넣어 복사하는 것을 얕은 복사라고 한다.

이렇게 될 경우 두 변수 모두 값에 접근하여 객체의 상태를 변경 할 수 있다. ( => 수정 가능하다)

 

깊은 복사 Deep: 깊은 copy 복사

깊은 복사된 객체는 객체안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.

한 데이터의 공유가 아닌, 똑같은 구조의 객체를 하나 더 생성하여 따로 사용하고자 할 때 사용하는 Copy 방법


1. Object.assign() assign: 맡기다 배치하다

 

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

빈 Object에다가 복사를 하려는 Object를 병합한다. 그러면 형태는 originObj이지만 실제로는 빈 Object와 originObj가 병합된 새로운 Object가 반환된다.

 

실제 객체는 다른 곳에 있고 변수는 다른 곳에 있는 객체의 주소 값을 저장해 참조한다.

변수에 변경 사항을 지시한다면 주소의 값으로 찾아가 그 객체의 상태를 변경한다.

근데!!! 객체 안의 객체도 마찬가지다.. 그도 다른 객체에 할당하고 주소 값을 가지게 된다.

※ 여기에 Object.assign()의 함정이 숨어 있다!!

const obj = {
  a: "a",
  num: {
    one: 1,
    two: 2,
  },
};

const shllowCopy = Object.assign({}, obj);
console.log(shllowCopy); //{ a: 'a', num: { one: 1, two: 2 }}
console.log(shllowCopy === obj); // false
console.log(shllowCopy.num === obj.num); // true
shllowCopy.a = "A";
console.log(shllowCopy); //{ a: 'a', num: { one: 1, two: 2 }}
console.log(obj);
shllowCopy.num.one = "one";
console.log(obj); //{ a: 'a', num: { one: 'one', two: 2 } }

obj === shllowCopy false 이지만

obj.num === shllow.num 은 true가 나온다. 

객체 안의 객체는 같은 주소를 참조 하고 있는 것이다.

Object.assign()은 깊은 복사인가 얕은 복사인가.. 그 중간인가 ㅠㅠㅠ

 

2. 전개 연산자 ( Spread Operation)

전개 연산자도 마찬가지이다.

 

const shllowCopy2 = { ...obj };

console.log(shllowCopy2); //{ a: 'a', num: { one: 1, two: 2 }}
console.log(shllowCopy2 === obj); // false
console.log(shllowCopy2.num === obj.num); // true
shllowCopy2.a = "A";
console.log(shllowCopy2); //{ a: 'a', num: { one: 1, two: 2 }}
console.log(obj);

shllowCopy2.num.one = "one";
console.log(obj); //{ a: 'a', num: { one: 'one', two: 2 } }

 

깊은 복사가 된 것은 제일 바깥의 Depth뿐이다.

 

※ 객체 안의 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이것은 얕은 복사이다!!


찐 깊은 복사

객체 안의 객체가 있을 때 그 안의 객체 모두!! 원본 객체와의 참조가 완전히 끊어진 객체!!

 

1. 재귀 함수를 이용한 복사

객체의 프로퍼티는 변수고

결국에 변수 안에 원시 값이 담기는 것이다.

원시 값은 항상 깊은 복사 이므로

각 객체 내의 프로퍼티를 모두 깊은 복사로 따와버린다!! 자기 자신을 호출  함으로 써!! 모든 객체를!!

function deepCopy(obj) {
  const deep = {};

  for (let key in obj) {
    if (typeof obj[key] === "object") {
      deep[key] = deepCopy(obj[key]);
    } else {
      deep[key] = obj[key];
    }
  }
  return deep;
}

const copiedObj = deepCopy(obj);

console.log(copiedObj); // { a: 'a', b: { c: 2 } }
console.log(copiedObj === obj); // false
copiedObj.b.c = "c";
console.log(copiedObj); // { a: 'a', b: { c: 'c' } }
console.log(obj); // { a: 'a', b: { c: 2 } }

2. JSON.stringify()

JSON.stringify()로 객체를 json 문자열로 반환하는 과정에서 원본 객체와의 참조가 모두 끊어진다!!

이 후 JSON.parse()로 자바스크립트 객체로 바꿔주면 깊은 복사가 된다

 

... 매우 느리다고 알려져 있다.

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

console.log(copiedObj); // { a: 'a', b: { c: 2 } }
console.log(copiedObj === obj); // false
copiedObj.b.c = "c";
console.log(copiedObj); // { a: 'a', b: { c: 'c' } }
console.log(obj); // { a: 'a', b: { c: 2 } }

3. lodash 라이브러리 사용

// import _ from "lodash"
copiedObj = _.cloneDeep(obj);
console.log(copiedObj); // { a: 'a', b: { c: 2 } }
console.log(copiedObj === obj); // false
copiedObj.b.c = "c";
console.log(copiedObj); // { a: 'a', b: { c: 'c' } }
console.log(obj); // { a: 'a', b: { c: 2 } }
console.timeEnd(" ");

 

 

[Javascript] 얕은 복사, 깊은 복사

자바스크립트에서 값은 원시값과 참조값으로 나뉜다. 원시값 Number String Boolean Null Undefined 참조값 Object Symbol 원시값은 값을 복사 할 때 복사된 값을 다른 메모리에 할당 하기 때문에 원래의 값과

velog.io