Front-end/JavaScript

JS Type 2편 - 얕은 복사, 깊은 복사(shallow & deep clone)

파리외 개발자 2022. 12. 26. 23:29
//call by value
//각자 값에 대한 저장공간을 가진다
var a = 5;
var b = a;

b++;

console.log(a);
console.log(b);

원시형 타입의 변수는 저장공간에 값이 저장된다.

따라서 b에 a를 할당했을 때 저장공간에 있는 값인 5가 할당된다.

5가 할당된 후 b의 값을 1 증가시켜도 a의 값에는 변동이 없다.

//call by reference
let obj1 = { name: "Kim", password: "123" };
let obj2 = obj1; //obj1의 주소가 들어간다.

obj2.password = "easy";

console.log(obj1);
console.log(obj2);

참조형 타입의 변수는 값 대신에 주소가 저장된다.

만약 obj1을 obj2에 대입하게 된다면 값이 들어간 것처럼 보여도

사실은 주소가 들어갔으므로 이제부터 obj1과 obj2는 같은 주소를 가지고 같은 공간을 가리킨다.

obj2의 password만 바꿨을 뿐인데, 두 변수 모두 값이 변경됐다.

참조형 변수 타입은 주소를 저장하므로 대입식을 사용하면 변수의 값을 복사할 수 없다.

얕은 복사 (Shallow clone)

얕은 복사는 이러한 참조형 변수의 값을 복사하기 위해 사용한다.

array

//array
var c = [1, 2, 3];
var d = c;

d.push(999);

console.log(d);
console.log(c);

배열은 참조형 이기 때문에 대입식을 사용하면 같은 주소를 가리키게 된다.

var c = [1, 2, 3];
var d = [].concat(c);

d.push(111);

console.log(d);
console.log(c);

배열타입의 내장 메서드인 concat을 사용해 d에 할당해 봤다.

concat을 사용해 복사된 배열 d는 c와 별개의 주소를 가지게 된다.

object

//obj
let obj = { a: "a", b: "b", c: { deep: "try copy" } };
//shallow clone
let clone = Object.assign({}, obj);
let clone2 = { ...obj };

obj.b = 5;

console.log(obj);
console.log(clone);
console.log(clone2);

이번에는 객체를 내장메서드 assign과 ...(spread)을 사용해 얕은 복사를 진행했다.

전개 연산자인 spread는 배열을 복사할 때도 사용할 수 있다.

얕은 복사는 객체에 들어있는 주소값을 한번 타고 들어가서 그 공간에 있는 값들을 가져와서 복사한다.

깊은 복사 (Deep clone)

let obj = { a: "a", b: "b", c: { deep: "try copy" } };
//shallow clone
let clone = Object.assign({}, obj);
let clone2 = { ...obj };

obj.b = 5;

console.log(obj);
console.log(clone);
console.log(clone2);

obj.c.deep = "hahahah";

console.log(obj);
console.log(clone);
console.log(clone2);

얕은 복사는 주소값을 한번까지밖에 타고 들어가지 않기 때문에 

만약 주소층이 둘 이상이라면 그 이상부터는 다시 주소값을 가져오고

같은 공간을 가리키게 된다.

c속성에 저장된 객체의 deep속성의 주소값이 얕은 복사로 복사되었기 때문에

c는 모두 같이 변동된다.

//deep clone
let superClone = JSON.parse(JSON.stringify(obj));

obj.c.deep = "heheh";

console.log(obj);
console.log(clone);
console.log(superClone);

이럴 땐 어쩔 수 없이 모든 주소층을 다 뒤져서 통째로 복사해오는 수밖에 없다.

JSON 내장 메서드를 사용해 obj를 문자열로 변환하고 파싱한 결과를 변수에 저장했다.

진정한 의미의 복사다.

깊은 복사가 된 객체는 obj의 변동값에 영향을 안 받는 것을 확인할 수 있다.

하지만 이러한 깊은 복사는 배열이 깊어지거나 데이터의 양이 많아질수록 비효율적인 행위가 될 수 있으니

정말 필요한 경우가 아니라면 사용을 지양하거나 본인의 상황에 맞는 함수를 제작해 복사하는 것이 좋다.