[JS] 깊은복사 / 얕은복사

2021. 11. 30. 09:27Javascript/개념

 

1.깊은복사 (원시타입) / Call By Value(값에 의한 호출)

원시 타입(primitive type)의 데이터가 복사 될때는 새로운 메모리 공간을 생성하여 메모리에 독립적인 값을 저장

let a = 1
let b = a

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

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

 

2. 얕은복사 (참조타입) / CallByReference

object(객체,배열, 함수)와 같은 참조 타입(reference type) 데이터는 애초에 저장 시 데이터에 대한 주소 (힙(Heap) 메모리의 주소값)가 저장되기 때문에 복사 시 값 자체가 아닌, 해당 값을 가리키는 주소가 복사된다.

원시타입(String, Number, Boolean)은 복사(깊은복사)가 되고, 원시타입을 제외한 객체들(참조타입)은 참조(얕은복사)가 된다. 

let a = { name : 'min' }
let b = a    // 참조

console.log(a)  // { name: 'min' }
console.log(b)  // { name: 'min' }

a.name = 'kyeong min'

console.log(a)  // { name: 'kyeong min' }
console.log(b)  // { name: 'kyeong min' }

변수 a에 객체를 하나 만들고 b에 a를 할당해 주었다.

그후 a의 name이란 키의 값을 'kyeong min'으로 변경했더니, a객체를 복사한 b의 객체에서도 변형이 일어났다.

이는 참조 타입의 변수는 실제 데이터가 저장된 주소를 참조하기에 복사가 일어났다 하더라도,

a와 b 둘다 가리키고 있는 방향(주소)이 동일하기에 이런 일이 일어나는 것이다.

즉, 데이터가 그대로 하나 더 생성된 것이 아닌 해당 데이터의 메모리 주소를 전달하게 돼서,

결국 한 데이터를 공유하게 되는 것이다.

 

 

3. 참조타입의 깊은 복사

let obj = {
  a: 1,
  b: 2,
  c: {name : 'min'}
}

let copy = {...obj}

obj.a = 100
obj.c.name = 'kyeong min'

console.log(obj.a)  // 100   
console.log(copy.a) // 1   <- copy 객체에서 숫자는 복사가 되지 않았음.

console.log(obj.c)   // { name: 'kyeong min' }
console.log(copy.c)  // { name: 'kyeong min' }  <- copy 객체에서 객체는 복사가 됨.

물론 obj객체의 a란 키에 100을 재할당 후 b의 a를 출력했을때,

100이 아닌, 1로 출력되기에 정상적으로 깊은 복사가 이루어진 것 처럼 보이지만,

obj 내부의 {name : "kyeong min"}이란 객체의 'name'의 값을 "kyeong min"으로 변경했더니

b의 name까지 변경된 것을 알 수 있다. 즉, 완벽한 복사가 이루어졌다고 하기 힘들다.

 

=> Number는 복사X , Obj는 복사 O 

 

 

4. 그렇다면 완벽한 복사는 어떻게 해야할까?

- JSON 객체의 메소드를 이용

let obj = {
    a: 1,
    b: 2,
    c: {name: 'min'}
};

const copy = JSON.parse(JSON.stringify(obj));
console.log(copy); // { a: 1, b: 2, c: { name: 'min' } }

obj.a = 100;
obj.c.name = 'kyeong min';

console.log(obj.a); // 100
console.log(copy.a); // 1

console.log(obj.c); // { name: 'kyeong min' }
console.log(copy.c); // { name: 'min' } <- copy 객체에서 복사 되지 않았다!!!!!

JSON.stringify 메서드는 자바스크립트 객체를 JSON문자열로 변환 시킨다.

반대로 JSON.parse는 JSON문자열을 자바스크립트 객체로 변환 시킨다.

 

그렇기에 객체를 JSON문자열로 변환했다가 다시 객체로 변환하기에, 객체에 대한 참조가 사라진것이다.

그러나 이 방법에는 2가지 문제점이 있는데, 타 방법들에 비해 성능이 떨어진다는점과

JSON.stringify 메서드가 함수를 undefined로 처리한다는 점이다.

 

 

- Lodash의 _.cloneDeep 함수 

const _ = require('lodash');

let obj = {
    a: 1,
    b: 2,
    c: {name: 'min'},
    d: () => {
        return `hello`;
    }
};

const copy = _.cloneDeep(obj);
obj.c.name = 'kkm';

console.log(obj);  // { a: 1, b: 2, c: { name: 'kkm' }, d: [Function: d] }
console.log(copy); // { a: 1, b: 2, c: { name: 'min' }, d: [Function: d] }
console.log(copy.d()); // hello

lodash 라이브러리의 cloneDeep() 메서드를 이용하는 방법이 가장 간편하다고 한다.

위 예제에서 알 수 있듯이 JSON변환을 이용한 방식에서 일어났던 함수 undefined 처리 문제가 해결된것을 알 수 있다.

 

 

 

 

 

 

'Javascript > 개념' 카테고리의 다른 글

[JS] 마우스 이벤트 종류  (0) 2021.12.10
[JS] 이벤트 버블링 / 캡쳐링 / 위임  (0) 2021.11.30
[JS] 원시타입 / 참조타입 (객체,배열비교)  (0) 2021.11.30
[JS] 배열 메소드  (0) 2021.11.16
[JS] 문자열 메소드  (0) 2021.11.16