📖 [강의 내용 및 개념 정리]
목차
변수는 주소를 저장하고, 주소는 특별한 동적인 데이터 보관함에 보관되는데 이 데이터 보관함을 메모리 힙 이라고 합니다.
값을 재할당 할 경우 주소는 참조한 모든 값이 영향을 받습니다. 즉, 값이 공유됩니다.
(출처) tistory - 원시 타입(primitive type) vs 참조 타입(preference type) 요약
위 원시 자료형 특징에서 원시 자료형은 변경할 수 없는 값이라고 했다.
그렇다면 어떻게 재할당을 할 수 있는 것일까?
재할당을 한 경우 값이 변경된 것처럼 보이는 것이다.
let str = 'hello';
str = 'world';
이 경우 메모리 내부에는 원시 값을 저장하기 위한 새로운 공간을 확보한 뒤, 그 공간에 str
이라는 변수명 이름을 붙이고 'world'를 추가로 저장하는 것이다.
즉, 변수에 다른 값을 재할당해도 원시 값 자체가 변경된 것이 아니라 새로운 원시 값을 생성하고, 변수가 다른 메모리 공간을 참조한다.(불변하는 읽기 전용 데이터)
사용하지 않는 값은 가비지 콜렉터가 자동으로 메모리에서 삭제한다.
let num2 = num;
num2 = 5;
num2의 값은 num과 같은 3이 할당되었다.
그림 속 원시 자료형 스택에 변수 명: num2, 변수 값: 3
이 하나가 더 생겼을 것이다.
이후 5로 재할당되었다.
스택 속 변수 명: num2
인 것을 찾고 변수 값: 5
로 재할당해주었을 것이다.
num의 변수 값은 여전히 3이다.
let numArr = arr;
numArr.push(7);
numArr에는 변수 arr이 할당되었다.
변수 arr에는 주소: 1
값을 저장하고 있다.
즉, 스택에는 변수명: numArr, 주소: 1
이 하나 더 생겼을 것이다.
이후 numArr에 7이라는 값을 추가하였다.
스택 속 변수 명: numArr
을 찾고, 주소: 1
을 따라가 해당 주소에 저장된 값들에 7을 추가해주었다.
numArr의 값만 변한 것이 아니라 arr의 값도 변화하였다.
위에서 설명했듯 값이 공유되는 것이다.
let numArr = [...arr];
spread operator를 사용하면 기존 불변성(immutable)을 유지하기 때문에 많이 사용하는 방법이다.
[Symbol.iterator]
프로퍼티가 존재한다면 이터러블한 것let numArr = arr.slice();
새로운 배열을 반환하는 함수들(slice, map, filter ...)등을 적절히 사용하는 것도 방법이다.
let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = Object.assign({}, obj);
console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false
참조 자료형 내부에 참조 자료형이 중첩되어 있는 경우, slice(), Object.assign(), spread syntax를 사용해도 참조 자료형 내부에 참조 자료형이 중첩된 구조는 복사할 수 없다.
참조 자료형이 몇 단계로 중첩되어 있던지, 위에서 설명한 방법으로는 한 단계까지만 복사할 수 있다.
위 결과 이유에 대한 사진 설명
먼저 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환하고, 반환된 값에 다시 JSON.parse()를 사용하기
하지만 예외가 존재
JSON.stringify()
와 JSON.parse()
가 처리하지 못하는 예외 사항을 처리할 수 있음import { equals } from 'ramda';
// 입력값이 같은지, 같이 않은지 체크하는 메서드
const checker = (...args) => console.log(equals(...args));
"데이터를 깊게 & 얕게 때에 따라 잘 복사해야 한다"
자바스크립트의 내장 기능만으로는 얕은 복사밖에 수행할 수 없다.
- spread operator, Array.prototype.slice(), Object.assign()
JSON.stringify()
와JSON.parse()
을 통해 깊은 복사를 구현할 수 있으나 예외가 존재한다.
- BigInt, Function, undefined
라이브러리를 사용한 깊은 복사
- lodash와 ramda는 깊은 복사에 대한 목표를 달성하기 위해 퍼포먼스가 떨어지더라도 모든 경우의 수를 체크하여 정확성에 의미를 둠
자바스크립트에 왜 깊은 복사를 수행할 수 있는 메서드가 존재하지 않을까?
https://github.com/tc39/ecma262/issues/1319
-> 깊은 복사는 아직 일반적인?통용되는? 알고리즘이 언어에 아직 존재하지 않는 구조화된 복사가 아니다.
https://github.com/tc39/ecma262/issues/1840
결론: 깊은 복사를 할 수 있도록 언어 내부적으로 모델링되어 있지 않기에 내장 메서드 대신 외부 라이브러리를 통해서 깊은 복사를 구현할 수 있다.
하지만 가장 베스트는, 깊은 복사를 하기 전에 과연 깊은 복사를 무리하면서까지 해야하는지 아키텍쳐 관점에서 다시 한 번 생각해봄이 좋지 않을까