- Published on
2024.02.21
[딥다이브] - 11. 원시 값과 객체의 비교
11.1. 원시 값
11.1.1. 변경 불가능한 값
- 원시 타입의 값: 변경 불가능한 값, 읽기 전용 값
원시 값은 변경 불가능하다는 것은 원시 값 자체를 말하는 것이지 변수 값을 변경할 수 없다는 얘기는 아니다. 변수는 재할당을 통해 변수 값을 변경할 수 있다.
상수는 재할당이 금지된 변수로 변수 값을 변경할 수 없다. 상수와 변경 불가능한 값을 동일시하면 안 된다.
원시 값을 재할당하면 메모리 공간을 새로 확보하고 재할당한 원시 값을 저장한 후, 변수가 참조하는 메모리 공간의 주소를 바꾼다.
이러한 불변성 덕분에 데이터의 신뢰성이 보장된다.
11.1.2. 문자열과 불변성
자바스크립트의 문자열은 원시 타입이며, 변경 불가능하다.
문자열은 유사 배열 객체이면서 이터러블이므로 배열과 유사하게 접근할 수 있다. 원시 값을 객체처럼 사용하면 원시 값을 감싸는 래퍼 객체로 자동 변환되는데, 문자열은 원시 값이므로 변경할 수 없다.
유사 배열 객체
인덱스를 통해 프로퍼티 값에 접근할 수 있고, length 프로퍼티를 갖는 객체를 말한다.
11.1.3. 값에 의한 전달
변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수에는 할당되는 변수의 원시 값이 복사되어 전달된다. 이를 값에 의한 전달이라 하는데, 값이 복사되어 전달되어 두 변수의 값은 다른 메모리 공간에 저장된 별개의 값이 된다. 어느 한 쪽의 값을 변경해도 다른 변수의 값에는 영향을 주지 않는다.
11.2. 객체
객체는 프로퍼티의 개수가 정해져 있지 않고, 동적으로 추가되거나 삭제할 수 있어 메모리 공간의 크기를 사전에 정해 둘 수 없다. 객체가 생성된 이후에는 프로퍼티를 삭제하거나 추가할 수 없는 자바, C++같은 클래스 기반 객체지향 프로그래밍 언어와는 달리 자바스크립트는 클래스 없이 객체를 생성할 수 있고, 객체가 생성된 이후에도 동적으로 프로퍼티와 메서드를 추가할 수 있다.
11.2.1. 변경 가능한 값
- 객체 타입의 값: 변경 가능한 값
객체를 할당한 변수에는 객체가 저장된 메모리 공간의 주소가 저장되어 있는데, 이를 참조 값이라 한다. 원시 값과 달리 객체는 변경 가능한 값이다. 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다. 프로퍼티를 동적으로 추가하거나 값을 갱신하거나 프로퍼티 자체를 삭제할 수도 있다. 객체는 원시 값과는 달리 여러 개의 식별자가 하나의 객체를 공유할 수 있다.
얕은 복사
한 단계까지만 복사하는 것, 객체에 중첩되어 있는 개체의 경우 참조 값을 복사한다. 객체를 할당한 변수를 다른 변수에 할당하는 것을 얕은 복사라고 부르는 경우도 있다.
const obj = { x: { y: 1 } }
const shallowCopy = { ...obj }
console.log(shallowCopy === obj) // false
console.log(shallowCopy.x === obj.x) // true
깊은 복사
객체에 중첩되어 있는 객체까지 모두 복사하는 것, 객체에 중첩되어 있는 객체까지 모두 복사하여 완전한 복사본을 만든다. 원시 값을 할당한 변수를 다른 변수에 할당하는 것을 깊은 복사라고 부르는 경우도 있다.
lodash의 clonedeep 함수
var objects = [{ a: 1 }, { b: 2 }]
var deep = _.cloneDeep(objects)
11.2.2. 참조에 의한 전달
객체를 가리키는 변수를 다른 변수를 할당하면 원본의 참조 값이 복사되어 전달되는데 이를 참조에 의한 전달이라 한다. 원본과 사본은 저장된 메모리 주소는 다르지만 서로 동일한 참조 값을 갖는데 두 개의 식별자가 하나의 객체를 공유하는 것을 의미한다. 어느 한 쪽의 값을 변경하면 서로 영향을 주고받는다.
깊은 복사
깊은 복사와 관련해서 MDN 문서
JavaScript에서, 모든 표준 내장 객체의 복사 작업(전개 구문, Array.prototype.concat(), Array.prototype.slice(), Array.from(), Object.assign(), Object.create())은 깊은 복사본을 생성하지 않습니다 (대신, 얕은 복사본을 생성합니다).
직렬화가 가능하다면 JavaScript 객체의 깊은 복사를 만드는 한 가지 방법은, JSON.stringify()를 사용하여 객체를 JSON 문자열로 변환한 다음, JSON.parse()로 문자열을 다시 (완전히 새로운) JavaScript 객체로 변환하는 것입니다.
const ingredientsList = ['noodles', { list: ['eggs', 'flour', 'water'] }]
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList))
위 코드의 객체는 직렬화할 수 있을 만큼 단순하지만, 많은 JavaScript 객체는 직렬화할 수 없습니다.
예를 들어, 함수 (클로저 포함), 심볼, HTML DOM API에서 HTML 요소를 나타내는 객체, 재귀 데이터 등등.
이 경우 JSON.stringify()를 호출하면 실패합니다.
Web API structuredClone()은 또한 깊은 복사본을 만들 수 있습니다. 또한 Error와 같은 더 많은 데이터 타입을 처리합니다. 그러나 structuredClone()은 JavaScript 언어 자체의 기능이 아니라 web API를 실행하는 브라우저 및 다른 JavaScript 호스트의 기능입니다. 그리고 직렬화할 수 없는 객체에 structuredClone()을 호출하면, JSON.stringify()과 같이실패할 것입니다.