
원시 자료형을 할당한 변수를 다른 변수에 할당하면 값 자체의 복사가 일어난다.
값 자체가 복사된다는 것은 하나의 값을 변경해도 다른 하나에 영향을 미치지 않는다는 것이다.
let n1 = 1;
let n2 = n1;
console.log(n1); // 1
console.log(n2); // 1
console.log(n1 === n2) //true
n2 = 2;
console.log(n1); // 1
console.log(n2); // 2
console.log(n1 === n2); //false
참조 자료형은 임의의 저장공간(heap)에 값을 저장하고 그 저장공간을 참조하는 주소를 메모리에 저장한다.
따라서 다른 변수에 할당할 경우 값 자체가 아닌 메모리에 저장되어 있는 주소가 복사된다.
let arr1 = [1, 2, 3];
let arr2 = arr;
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); //true
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [1, 2, 3, 4]
console.log(arr1 === arr2); // true
참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하기 때문에 하나의 값을 바꾸면 다른 하나도 영향을 받는다.
배열을 복사하는 방법은 배열 내장 메소드인 slice()를 이용하는 방법과 spread 문법을 사용하는 방법이 있다.
let arr1 = [1, 2, 3];
let arr2 = arr1.slice();
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false
참조하고 있는 주소가 다르기 때문에 false가 나온다.
arr2.push(4)
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]
주소가 다르기 때문에 하나의 배열에 요소를 추가해도 다른 배열에 영향을 미치지 않는다.
spread 문법은 ES6에서 새롭게 추가된 문법으로, 배열을 펼칠 수 있다.
let arr1 = [1, 2, 3];
let arr2 = [...arr1];
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); //false
arr2.push(4);
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]
객체를 복사할 때는 Object.assgin()을 사용하는 방법과 spread 문법을 사용하는 방법이 있다.
let obj1 = {
name: 'Bob',
age: 30
};
let obj2 = Object.assign({}, obj1);
console.log(obj2); // { name: 'Bob', age: 30 }
console.log(obj1 === obj2); //false
let obj1 = {
name: 'Bob',
age: 30
};
let obj2 = {...obj1};
console.log(obj2); // { name: 'Bob', age: 30 }
console.log(obj1 === obj2); // false
참조 자료형을 복사할 때에는 예외 상황이 있다.
참조 자료형 내부에 참조 자료형이 중첩된 경우, 중첩된 구조는 복사할 수 없다.
참조 자료형이 몇 단계로 중첩되어 있던지 위에서 배운 slice(), Object.assign(), spread 방법으로는 중첩된 구조의 한 단계까지만 복사할 수 있다.
let users = [
{
name: 'Bob',
age: 30
},
{
name: 'Jain',
age: 35
},
{
name: 'Jhon',
age: 40
}
];
let copied = users.slice();
console.log(users === copied); // false
users와 copied는 각각 다른 주소를 참조하기 때문에 false가 반환된다.
console.log(users[0] === copied[0]); // true
하지만 users와 copied의 0번째 요소를 비교하면 true가 반환된다.
각 요소는 여전히 같은 주소값을 참조하기 때문이다.
이처럼 slice(), Object.assign(), spread 등의 방법으로 참조 자료형을 복사하면 중첩된 구조의 한 단계까지만 복사한다.
이를 얕은 복사(shallow copy) 라고 한다.
얕은 복사와 달리 참조 자료형 내부에 중첩된 모든 참조 자료형을 복사하는 것을 깊은 복사 라고 한다.
JavaScript 내부적으로는 깊은 복사를 수행할 수 있는 방법이 없기 때문에 JavaScript의 다른 문법을 응용하여 깊은 복사를 수행 할 수 있다.
JSON.stringfy() 는 참조 자료형을 문자열 형태로 변환하여 반환한다.
JSON.parse() 는 문자열의 형태를 객체로 변환하여 반환한다.
중첩된 참조 자료형을 JSON.stringfy()를 사용해 문자열 형태로 반환하고, 반환된 값에 JSON.parse() 를 사용하면 깊은 복사와 같은 결과를 반환할 수 있다.
const arr = [1, 2, [3, 4]];
const copied = JSON.parse(JSON.stringfy(arr));
console.log(arr); // [1, 2, [3, 4]]
console.log(copied); // [1, 2, [3, 4]]
console.log(arr === copied); // false
console.log(arr[1] === copied[1]); // false
이 방법으로 깊은 복사를 할 수 있어 보이지만, 이 경우에도 예외가 있다.
중첩된 자료형 중 함수가 포함된 경우 위 방법을 사용하면 함수가 null로 바뀌게 된다.
따라서 완전한 깊은 복사 방법이라고 보기는 어렵다.
반드시 완전한 깊은 복사를 해야 하는 경우라면 node.js 환경에서 외부 라이브러리인 lodash 또는 ramda 를 설치하면 된다.
lodash 와 ramda 는 각각의 방법으로 깊은 복사를 구현해 두었다.
// lodash를 사용한 예시
const lodash = require('lodash');
const arr = [1, 2, [3, 4]];
const copied = lodash.cloneDeep(arr);
console.log(arr); // [1, 2, [3, 4]]
console.log(copied); // [1, 2, [3, 4]]
console.log(arr === copied); // false
console.log(arr[1] === copied[1]); // false
slice() 또는 spread 문법으로 복사할 수 있다.Object.assign() 또는 spread 문법으로 복사할 수 있다.JSON.stringfy(), JSON.parse()를 사용하는 방법이 있지만 참조 자료형 내부에 함수가 중첩된 경우 예외가 발생한다.lodash 또는 ramda 를 사용할 수 있다.