얕은 복사(shallow copy) 와 깊은 복사(deep copy)

신보연·2023년 3월 2일
0

얕은 복사(shallow copy)

  • 객체의 참조값(주소 값)을 복사
const obj1 = { a: 1, b: 2};
const obj2 = obj1;
console.log( obj1 === obj2 ); // true

배열 복사하기

slice():

배열 내장 메서드인 slice()를 사용하면 원본 배열을 복사할 수 있다.

let arr = [0, 1, 2, 3];
let copiedArr = arr.slice();
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false

새롭게 생성된 배열은 원본 배열과 같은 요소를 갖지만 참조하고 있는 주소는 다르다.
주소가 다르기 때문에 복사한 배열에 요소를 추가해도 원본 배열에는 추가되지 않는다.

copiedArr.push(4);
console.log(copiedArr); // [0, 1, 2, 3, 4]
console.log(arr); // [0, 1, 2, 3]

spread syntax:

spread라는 단어의 뜻처럼 배열을 펼칠 수 있다. 펼치는 방법은 배열이 할당된 변수명 앞에 ... 을 붙여주면 된다. 배열을 펼치면 배열의 각 요소를 확인할 수 있다.
배열뿐만 아니라 객체를 복사할 때도 사용할 수 있다.
그러나 참조 자료형 내부에 참조 자료형이 중첩되어 있는 경우, slice(), Object.assign(), spread syntax를 사용해도 참조 자료형 내부에 참조 자료형이 중첩된 구조는 복사할 수 없다. 참조 자료형이 몇 단계로 중첩되어 있던지, 위에서 설명한 방법으로는 한 단계까지만 복사할 수 있다.

let arr = [0, 1, 2, 3];

console.log(...arr); // 0 1 2 3


et arr = [0, 1, 2, 3];
let copiedArr = [...arr];
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false

객체 복사하기

Object.assign():

소스 개체에서 대상 개체로 모든 열거 가능한 속성과 자체 속성을 복사, 대상 객체를 반환.
소스 개체의 getter와 대상의 setter를 호출, 새 속성을 복사하거나 정의하지 않고 속성만 할당.

let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = Object.assign({}, obj);

console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false

깊은 복사(deep copy):

  • 객체의 실제 값을 복사
  • 참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것

JSON.stringify()와 JSON.parse():

JSON.stringify()는 참조 자료형을 문자열 형태로 변환하여 반환하고, JSON.parse()는 문자열의 형태를 객체로 변환하여 반환. 먼저 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환하고, 반환된 값에 다시 JSON.parse()를 사용하면, 깊은 복사와 같은 결과물을 반환.

const arr = [1, 2, [3, 4]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

간단하게 깊은 복사를 할 수 있는 것처럼 보이지만, 이 방법 또한 깊은 복사가 되지 않는 예외가 존재한다. 대표적인 예로 중첩된 참조 자료형 중에 함수가 포함되어 있을 경우 위 방법을 사용하면 함수가 null로 바뀌게 되고 따라서 이 방법 또한 완전한 깊은 복사 방법이라고 보기 어렵다.

const arr = [1, 2, [3, function(){ console.log('hello world')}]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, function(){ console.log('hello world')}]]
console.log(copiedArr); // [1, 2, [3, null]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

외부 라이브러리 사용

완전한 깊은 복사를 반드시 해야 하는 경우라면, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 설치, lodash와 ramda는 각각 방법으로 깊은 복사를 구현.

const lodash = require('lodash');

const arr = [1, 2, [3, 4]];
const copiedArr = lodash.cloneDeep(arr);

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

lodash의 cloneDeep을 사용한 깊은 복사의 예시

0개의 댓글