원시 값과 참조 값의 비교

자바스크립트가 제공하는 7가지 데이터 타입은 크게 원시 타입과 참조 타입으로 구분할 수 있다. 데이터 타입을 원시 타입과 참조 타입으로 구분하는 이유는 무엇일까?

원시 값

변경 불가능한 값

기본형 타입의 값은 원시 값으로 변경 불가능한 값이다.

변경 불가능하다는 것은 변수가 아니라 값에 대한 의미이다. 즉 "원시 값이 변경 불가능하다" 라는 말은 원시 값 자체를 변경할 수 없다는 것이지 변수의 값을 변경할 수 없다는 말이 아니다.

변수는 언제든 값을 재할당하여 변경할 수 있다. 상수또한 재할당이 금지된 변수일 뿐이다.

원시 값을 할당한 변수에 새로운 원시 값을 재할당하면 메모리 공간에 저장되어 있는 원시 값을 변경하는 것이 아니라 새로운 메모리 공간을 확보하고 새로운 원시 값을 저장한다. 변수는 이 새롭게 저장된 원시 값을 가리킨다.

let count = 1;
count = 2;

Pasted image 20231013195919.png|300
count변수에 처음 1이 할당 되었다.

Pasted image 20231013200048.png|300
이후 count변수에 2가 재할당될 때 기존 가리키고 있던 메모리의 값이 변하는 것이 아닌 새로운 메모리 공간을 만들고 그 곳을 가리킨다.
값의 이러한 특성을 불변성이라 한다.

아무도 가리키지 않게 된 메모리 공간은 추후 가비지컬렉터에 의해 사라진다.

문자열과 불변성

자바스크립트의 장점 중 하나로 문자열을 원시 타입으로 제공한다는 것이 있다. 자바스크립트의 문자열은 유사 배열 객체이면서 이터러블이다. 따라서 배열과 유사하게 각 문자에 접근할 수 있다.

문자열은 원시형인데 배열이라고..??
원시 값을 객체처럼 사용하면 원시 값을 감싸는 래퍼 객체(Wrapper Object)로 자동 변환된다.

따라서 문자열은 배열에 접근하는 것처럼 요소를 선택할 수 있다.

let str = "string";
str[0] = "x";
console.log(str); // "string"

그러나 값을 변경해도 문자열은 원시값이기 때문에 바뀌지 않는다. 이는 데이터의 신뢰성을 보장한다.

값에 의한 전달

let score = 10;
let copy = score;

console.log(score, copy); // 10 10

copy = 20;
console.log(score, copy); // 10 20

변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수(copy)는 할당되는 변수(score)의 값이 복사되어 전달된다.

score변수와 copy변수의 값은 다른 메모리 공간에서 별개의 값이다. 따라서 copy의 값이 재할당 되어도 score의 값에는 변화가 없다.
Pasted image 20231013203731.png|250

"값에 의한 전달"이라는 용어는 오해가 있을 수 있다. 왜냐하면 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달되기 때문이다. 중요한 것은 변수에 원시 값을 갖는 변수를 할당하면 결국은 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한쪽에서 간섭할 수 없다는 것이다.

객체

자바스크립트의 원시 값을 제외한 것은 모두 객체이며 이는 모두 참조 값이다.
객체는 복합적인 자료구조로, 객체를 관리하는 방식이 원시값에 비해 복잡하고 구현 방식도 브라우저마다 다를 수 있다. 원시 값은 상대적으로 적은 메모리를 사용하지만 객체는 원시값에 비해 비용이 많이 든다. 따라서 객체는 원시 값과는 다른 방식으로 동작하도록 설계되어 있다.

변경 가능한 값

객체(참조) 타입의 값은 변경 가능한 값이다.

원시 값을 할당한 변수는 원시 값 자체를 값으로 갖는다. 하자만 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있다. 참조 값은 생성된 객체가 저장된 메모리 공간의 주소이다.

일반적으로 원시 값을 할당한 변수의 경우 "변수는 2라는 값을 갖는다" 라고 한다. 하지만 객체를 할당한 변수의 경우는 "변수는 객체를 참조하고(가리키고) 있다" 라고 한다.

let member = {
	name: "sanghyeon"
}

Pasted image 20231013205057.png|300

객체는 원시 값과 달리 재할당 없이 값을 직접 변경할 수 있다. 즉, 재할당 없이 프로퍼티를 동적으로 추가하고, 갱신하고, 삭제할 수 있다.

Pasted image 20231013211818.png|300

객체를 생성하고 관리하는 방식은 매우 복잡하며 비용이 많이 드는 일이다. 객체를 변경할 때마다 원시 값처럼 새로 복사를하면 좋겠지만 객체는 크기가 매우 클 수도 있고, 크기가 일정하지도 않다. 다시 말해 매번 복사를 하면 메모리의 효율적 소비가 어렵고 성능이 나빠진다.

따라서 메모리를 효율적으로 사용하기 위해 객체는 변경 가능한 값으로 설계되어 있다.

객체는 이러한 구조적 단점에 따른 부작용이 있다. 그것은 원시 값과는 다르개 여러 개의 식별자가 하나의 객체를 공유할 수 있다라는 것이다.

💡 얕은 복사와 깊은 복사
객체를 프로퍼티 값으로 갖는 객체의 경우 얕은 복사는 한 단계까지만 복사하는 것을 말하고, 깊은 복사는 객체에 중첩되어 있는 개체까지 모두 복사하는 것을 말한다.

참조에 의한 전달

let member = {
	name: "sanghyeon"
}

let copy = member;

위 처럼 membercopy에 복사를 하면 저장된 메모리 주소는 다르지만 동일한 참조 값을 갖는다. 다시 말해 원본 member copy 모두 동일한 객체를 가리킨다. 따라서 하나의 객체의 프로퍼티 값을 변경하면 서로 영향을 주고받는다.

Pasted image 20231013212305.png|350


참조