
우선 이 두가지를 이해하기위해선 원시값과 참조값에 대해서 알아야한다.
기본자료형(단순 데이터)을 의미한다.
Number, String, Boolean, Null, Undefined 등이 있다.
변수에 원시값을 저장하면 변수의 메모리 공간에 실제 값이 저장된다.
변수를 조작하려고하면 실제 저장된 값이 조작된다.
여러 자료형으로 구성된 메모리에 저장된 객체이다.
Object, Symbol등이 있다.
변수에 객체를 저장하면 독립적인 메모리 공간에 값을 저장하고, 변수에 저장된 메모리 공간의 참조(위치 값)를 저장한다.
그래서 할당된 변수를 조작하는 것은 사실 객체 자체를 조작하는 것이 아닌, 해당 객체의 참조를 조작하는 것이다.
위 두가지를 이해했다면 얕은복사와 깊은복사로 넘어가보자.
얕은복사는 객체의 참조값을 복사하는것이다.
주소값을 복사하기때문에 원본과 사본이 같은 주소값을 가리킨다.
원본이 바뀌면 사본이 바뀌고 사본이 바뀌면 원본이 바뀌는 문제점이 생길 수 있다.
const a = {
one: 1,
two: 2,
};
let b = a;
b.one = 3;
console.log(a); // { one: 3, two: 2 } 출력
console.log(b); // { one: 3, two: 2 } 출력
// 기존 값에 영향을 끼친다.
깊은복사는 실제 값을 복사하는 것이므로, 복사를하고 값을 수정해도 기존 원시값을 저장한 변수에는 영향이 없다.
const a = 'a';
let b = 'b';
b = 'c';
console.log(a); // 'a';
console.log(b); // 'c';
// 기존 값에 영향을 끼치지 않는다.
얕은 복사 방법의 대표적인 예, start부터 end 인덱스까지 기존 배열에서 추출하여 새로운 배열을 리턴하는 메서드이다. 만약 start와 end를 설정하지 않는다면, 기존 배열을 전체 얕은 복사한다. 라고 공식문서에 나와있다.
const original = ['a',2,true,4,"hi"];
const copy = original.slice();
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
copy.push(10);
console.log(JSON.stringify(original) === JSON.stringify(copy)); // false
console.log(original); // [ 'a', 2, true, 4, 'hi' ]
console.log(copy); // [ 'a', 2, true, 4, 'hi', 10 ]
slice는 중첩 구조 복사를 제대로 수행할 수 없다.
slice는 1 depth에서만 깊은 복사를 수행한다.
const original = [
[1, 1, 1, 1],
[0, 0, 0, 0],
[2, 2, 2, 2],
[3, 3, 3, 3],
];
const copy = original.slice();
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
// 복사된 배열에만 변경과 추가.
copy[0][0] = 99;
copy[2].push(98);
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
console.log(original);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
console.log(copy);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
얕은 복제(prototype 제외) 또는 객체의 병합은 이제 Object.assign() 보다 더 짧은 문법을 사용해 가능합니다. (공식문서)
var obj1 = { foo: "bar", x: 42 };
var obj2 = { foo: "baz", y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
slice와 마찬가지로 중첩구조 복사를 제대로 수행할 수 없다.
var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// 이제 배열 a 도 영향을 받음: [[], [2], [3]]
전개 연산자는 1 depth에서만 깊은 복사를 수행한다.
Object.assign() 메서드는 출처 객체들의 모든 열거 가능한 자체 속성 (en-US)을 복사해 대상 객체에 붙여넣습니다. 그 후 대상 객체를 반환합니다. (공식문서)
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// Expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target);
// Expected output: true
Object.assign도 slice와 spread operator와 마찬가지로 1 depth에서 깊은복사만 가능하다.
JSON.stringify()는 객체를 JSON 문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어진다.
객체를 JSON 문자열로 변환 후, JSON.parse()를 이용해 다시 원래 객체(자바스크립트 객체)로 만들어준다.
이 방법이 가장 간단하고 쉽지만 다른 방법에 비해 느리다는 것과 객체가 function일 경우, undefined로 처리한다는 것이 단점이다.
const object = {
a: "a",
number: {
one: 1,
two: 2,
},
arr: [1, 2, [3, 4]],
};
const copy = JSON.parse(JSON.stringify(object));
copy.number.one = 3;
copy.arr[2].push(5);
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }
const object = {
a: "a",
number: {
one: 1,
two: 2,
},
arr: [1, 2, [3, 4]],
};
function deepCopy(object) {
if (object === null || typeof object !== "object") {
return object;
}
// 객체인지 배열인지 판단
const copy = Array.isArray(object) ? [] : {};
for (let key of Object.keys(object)) {
copy[key] = deepCopy(object[key]);
}
return copy;
}
const copy = deepCopy(object);
copy.number.one = 3;
copy.arr[2].push(5);
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }
const deepCopy = require("lodash.clonedeep")
const object = {
a: "a",
number: {
one: 1,
two: 2,
},
arr: [1, 2, [3, 4]],
};
const copy = deepCopy(object);
copy.number.one = 3;
copy.arr[2].push(5);
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }