
평소 어렴풋이 알고 있던 Javascript에서의 copy by value와 copy by reference의 개념을 정확히 이해하기 위해서 내용을 정리하기로 했다.
JavaScript에서는 원시 타입(Primitive type)과 참조 타입(Reference type)이라는 두 가지 데이터 타입이 존재한다.
원시 타입 : Boolean, Null, Undefined, Number, String, Symbol
원시 타입의 경우 변수에 값을 할당하면 그 값이 메모리에 저장되며, 이를 'copy by value'라고 한다.
이는 변수에 값을 할당하는 것이 실제 값을 복사하여 새로운 메모리 공간에 저장하는 것을 의미한다.
// Copy by Value (원시 타입)
let a = 10;
let b = a;
b = 20;
console.log(a); // 10
console.log(b); // 20
위 코드 변수 a에는 원시 타입인 숫자 10이 할당되었다. 그리고 변수 b에 a를 할당하였다.
이 경우 a의 값 10이 복사되어 b에 할당된다. 따라서 후에 b의 값을 변경하더라도 a의 값은 변경되지 않는다.
참조 타입 : Object, Array, Function
참조 타입의 경우 변수에 값을 할당하면 그 값이 저장된 메모리 주소가 저장되며 이를 'copy by reference' 라고 한다.
이는 변수에 값을 할당하는 것이 실제 값이 아니라 값이 저장된 메모리 주소를 복사하는 것을 의미한다.
// Copy by Reference (참조 타입)
let a = { name: 'Kim' };
let b = a;
b.name = 'Lee';
console.log(a.name); // 'Lee'
console.log(b.name); // 'Lee'
위 코드 변수 a에는 참조 타입인 객체가 할당되었다. 그리고 변수 b에 a를 할당하였다.
이 경우 a의 값이 아니라 a의 값이 저장된 메모리 주소가 복사되어 b에 할당되게 된다.
따라서 후에 b의 값을 변경하면, 같은 메모리 주소를 참조하고 있는 a의 값도 함께 변경된다.
JavaScript에서 참조 타입인 객체를 'copy by value' 방식으로 복사하려면, 새로운 객체를 생성하고 원래 객체의 속성을 복사하는 방법을 사용해야 한다.
이는 얕은 복사(shallow copy) 와 깊은 복사(deep copy)의 방법으로 나눌 수 있다.
얕은 복사는 첫 번째 레벨의 속성만 복사하며, 그 이하의 레벨은 참조로 처리하게 된다.
따라서 복사된 객체의 속성 중 하나가 객체인 경우, 그 객체의 속성을 변경하면 원본 객체에도 영향을 미치게 된다.
그중 대표적으로 사용되는 얕은 복사는 Object.assign() 과 spread operator(...)이다.
let a = { name: 'Kim' };
// Object.assign() 메소드를 사용한 예
let b = Object.assign({}, a); // 새로운 객체에 a를 복사
b.name = 'Lee';
console.log(a.name); // 'Kim'
console.log(b.name); // 'Lee'
let a = { name: 'Kim' };
// 스프레드 연산자(spread operator)를 사용한 예
let b = { ...a };
b.name = 'Lee';
console.log(a.name); // 'Kim'
console.log(b.name); // 'Lee'
깊은 복사는 복사하려는 객체의 모든 레벨에서 새로운 객체를 생성하므로, 복사된 객체를 수정해도 원래 객체에는 영향을 미치지 않는다.
만일 객체의 모든 레벨에서 copy by value 방식의 복사를 원한다면, 깊은 복사(deep copy) 방식을 사용해야 한다.
깊은 복사의 대표적으로 사용되는 방법은 lodash 라이브러리의 _.cloneDeep() 함수이다.
// npm install lodash를 사용하여 설치 후 적용
const _ = require('lodash');
let a = { name: 'Kim', details: { age: 30 } };
let b = _.cloneDeep(a);
b.name = 'Lee';
b.details.age = 25;
console.log(a.name); // 'Kim'
console.log(a.details.age); // 30
console.log(b.name); // 'Lee'
console.log(b.details.age); // 25