[javascript] slice가 1차원 배열은 깊은 복사, 2차원 배열은 얕은 복사를 수행하는 이유

유재민·2022년 7월 19일
0

# 얕은 복사와 깊은 복사

자바스크립트에서 객체 복사 방법에는 얕은 복사 방법과 깊은 복사 방법이 있다.
얕은 복사란 객체를 복사할 때 객체만 복사하여 기존 객체와 복사된 객체가 같은 참조를 가리키는 복사를 말한다. 깊은 복사란 객체를 복사할 때 객체와 참조 값을 모두 복사하여 기존 객체와 복사된 객체의 참조가 완전히 끊어진 복사를 말한다.

  • 얕은 복사 방법 : slice(), Object.assign(), Spread 연산자()
  • 깊은 복사 방법 : JSON.parse(JSON.stringify(obj)), Lodash 라이브러리의 cloneDeep 메소드

자바스크립트에서 값은 원시값과 참조값 두 가지 데이터 타입의 값이 존재한다. 자바스크립트에서 원시 타입의 경우 값이 수정(새로운 값 할당)되면 새로운 메모리 공간에 독립적인 값을 저장하기 때문에 깊은 복사가 되고 참조 타입의 경우 값이 수정(새로운 요소 추가)되면 원본을 직접 수정하므로 얕은 복사가 된다.


# slice가 1차원 배열은 깊은 복사, 2차원 배열은 얕은 복사를 수행하는 이유

slice 메소드는 기본적으로 얕은 복사를 수행하지만 1차원 배열의 경우 값이 원시 타입이기 때문에 원시 타입에 새로운 값 할당 시 독립적인 메모리 공간을 생성하여 이 메모리 공간에 새로운 데이터가 추가되어 서로 다른 참조를 바라보게 된다. 그렇기 때문에 깊은 복사처럼 보인다.

하지만 2차원 배열의 경우 내부 배열도 참조 타입이기 때문에 메모리 힙에 독립적인 공간에 존재하지만 [[2차원 배열의 주소값], 원시 타입]과 같이 결국 내부 배열의 메모리 주소를 바라보고 있으므로 최종적으로 같은 참조를 바라보게 되는 것이다.

const test = [[1, 2, 3], 100];
const copy = test.slice();

test[0].push(10);
test[1] = 200;

console.log(test); // [[1, 2, 3, 10], 200]
console.log(copy); // [[1, 2, 3, 10], 100]

위 코드의 주소 값 참조는 아래와 같이 이루어진다. 이 때 변수가 참조하는 주소 값은 임의의 동네 이름으로 나타내보았다.

(1) test는 콜스택의 "주소: 광명1동 / 값: 소하1동" 의 주소를 가리키고, copy는 가리키는 콜스택의 "주소: 광명2동 / 값: 소하1동" 의 주소를 가리킨다.

(2) 위 test, copy의 값 필드에 소하1동은 메모리힙에 존재하며 "주소: 소하1동 / 값: [하안1동, 100]"과 같이 존재한다.

(3) 2차원 배열인 [1, 2, 3] 또한 참조 데이터이기 때문에 별도의 메모리 공간에 "주소: 하안1동 / 값: [1, 2, 3]" 과 같이 존재하게 되는 것이다.

(4) test[0].push(10) 이 실행되면 하안1동의 값 필드가 "주소: 하안1동 / 값: [1, 2, 3, 10]" 과 같이 변경되게 된다.

(5) test[1] = 200 이 실행되면 메모리에 존재하지 않던 원본 데이터가 새로 생성되므로 메모리 힙에 새로운 메모리 공간이 생성되고 기존에 존재하던 "주소: 소하1동 / 값: [하안1동, 100]"과 함께 "주소: 소하2동 / 값: [하안1동, 200]" 이 생성되게 된다.

(6) test는 콜스택의 "주소: 광명1동 / 값: 소하2동" 을 가리키게 되고 copy는 콜스택의 "주소: 광명2동 / 값: 소하1동" 을 가리키게 된다.

(7) 콜스택에서 참조하는 값은 다른 메모리힙에 데이터(소하1동, 소하2동)를 참조하므로 원본 데이터는 공유되지 않고 메모리힙에서는 같은 메모리힙에 데이터(하안1동)를 공유하고 있으므로 참조 데이터는 여전히 같은 데이터를 바라보고 있으므로 변경이 공유된다. 결과는 아래 이미지와 같다.

profile
프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2022년 11월 23일

안녕하세요! 재민님 블로그 참고하면서 많은 배움을 얻고 있습니다!
제가 지금 깊은복사와 얕은복사에 대해서 공부 중인데 궁금한 점이 생겨서 댓글 남깁니다 ㅠㅠ
제가 알기로는 객체는 모두 참조 값(변경 가능한 값)이고 객체에는 함수와 배열이 포함되어 있는걸로 알고 있는데 위 예제이서 1차원 배열의 경우 값이 원시 타입이라는 말씀이 헷갈려서 여기에 염치불구하고 댓글 남깁니다,,

답글 달기