자바스크립트의 타입은 원시 타입, 객체 타입으로 구분하는데
이 두 타입은 크게 세 가지 측면에서 다르다.
- 원시 값은 변경 불가능하지만 객체 타입은 변경 가능함
- 원시 값을 변수에 할당하면 변수엔 실제 값이 저장되지만 객체를 변수에 할당하면 참조 값이 저장됨
- 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달되고 (값에 의한 전달),
객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달됨 (참조에 의한 전달)
1. 원시 값
1-1) 변경 불가능한 값
한번 생성된 원시 값은 읽기 전용 값으로, 변경 불가능하다.
변수에 새로운 값을 넣으면
1. 새로운 메모리 공간을 만들고
2. 거기에 새 값을 저장한다
3. 변수가 새로운 공간을 가리키게 되면
4. 이전 메모리 공간은 버려진다.
왜 이렇게 하냐면
* 원시 값은 불변성을 가지고 있고
* 한번 저장된 값은 절대 바꿀 수 없다
* 또 새로운 값을 쓰려면 무조건 새 공간이 필요하다
다만, 변경 불가능하다는 말은 원시 값 자체를 변경할 수 없다는 얘기지
변수 값을 변경할 수 없다는 얘긴 아니다.
변수는 언제든지 재할당을 통해 변수 값을 교체할 수 있기 때문이다.
* 그러나 const 키워드를 사용해 선언한 변수는 재할당 금지됨
let message = "안녕"; // 문자열(원시 값) 생성
console.log(message); // "안녕" 출력
message = "Hello"; // 변수의 값을 새로운 문자열로 변경
console.log(message); // "Hello" 출력
원시 값 자체를 변경할 순 없지만, 변수는 언제든지 새로운 값을 담을 수 있기 때문에 변경 가능하다.
let greeting = "Hello";
greeting[0] = "h"; // 원시 값(문자열)은 변경 불가능!
console.log(greeting); // "Hello" 그대로 유지됨
이게 좋은 이유는
* 값이 언제 어떻게 바뀌는지 정확히 알 수 있고
* 예상치 못한 값 변경이 일어날 일이 없기 때문이다
1-2) 문자열과 불변성
원시 값을 저장하려면 먼저 확보해야 하는 메모리 공간의 크기를 결정해야 하는데,
이를 위해 원시 타입별로 메모리 공간의 크기가 미리 정해져 있다.
문자열은 다른 원시 값과 비교하면 독특한 특징이 있는데,
1개의 문자는 2바이트의 메모리 공간에 저장된다.
그래서 몇 개의 문자로 이뤄졌느냐에 따라 메모리 공간의 크기가 결정된다.
숫자 값은 1이든 100000이든 동일한 8바이트만 필요하지만, 문자열은 아니다.
이 같은 이유로 C에는 문자열 타입은 존재하지 않지만, 자바스크립트에는 문자열을 string 객체로 처리한다.
그리고 문자열은 유사 배열 객체이면서 이터러블이므로 배열과 유사하게 각 문자에 접근할 수 있다.
유사 배열 객체 -> 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체
1-3) 값에 의한 전달
score 변수에 숫자 값 80을 할당하고, copy 변수에 score 변수를 할당했을 때
score 변수에 새로운 숫자 값 100을 재할당하면 copy 변수의 값은 어떻게 될까?
copy변수에 80이 할당되면 이때 새로운 숫자 값 80이 생성되어 할당된다.
이처럼 변수에 원시 값을 갖는 변수를 할당하면,
할당 받는 변수에는 할당되는 변수의 원시 값이 복사되어 전달된다.
정리하자면 score 변수와 copy 변수의 값 80은 다른 메모리 공간에 저장된 별개의 값이다.

사진 설명을 입력하세요.
그래서 score 변수의 값을 변경해도 copy 변수에는 영향을 주지 않는다.
그리고 값에 의한 전달이라 설명되어 있지만 정확히는 변수에는 값이 아니라 메모리 주소가 전달된다.
단, 전달된 메모리 주소를 통해 메모리 공간에 접근하면 값을 참조할 수 있다.
2. 객체
객체는 프로퍼티 개수가 정해져 있지 않고 동적으로 추가, 삭제할 수 있다.
또한 프로퍼티 값에도 제약이 없어 객체는 메모리 공간의 크기를 사전에 정해둘 수 없다.
원시 값은 상대적으로 적은 메모리를 소비하지만 객체는 경우에 따라 크기가 클 수도 있어
원시 값과 비교할 때 비용이 많이 든다.
따라서 객체는 원시 값과는 다른 방식으로 동작하도록 설계되어 있다.
2-1) 변경 가능한 값
객체는 변경 가능한 값이다. 원시 값과 객체는 메모리에 저장되는 방식이 서로 다르다.
원시 값을 가진 변수는 그 값을 직접 가지고 있다. 즉, 변수가 실제 값을 바로 저장하고 있는 것이다.
반면 객체를 가진 변수는 객체가 저장된 메모리의 위치(주소)만을 가리키고 있다. 이렇게 객체의 위치를 가리키는 값을 참조값이라고 한다.
원시 값과 다르게 객체는 재할당 없이 객체를 직접 변경할 수 있다.
이는 객체가 변경 가능한 값으로 설계되어 있기 때문이다.
객체를 변경할 때는 새로운 메모리 공간을 만들지 않고, 기존 객체의 내용만 수정한다.
따라서 객체를 할당한 변수의 참조 값은 변경되지 않는다.
이런 설계는 장단점이 있다:
- 장점: 메모리를 효율적으로 사용할 수 있다.
- 단점: 여러 변수가 하나의 객체를 공유할 수 있어 예상치 못한 문제가 발생할 수 있다.
이러한 특성 때문에 객체를 복사하는 방법도 두 가지로 나뉜다:
1. 얕은 복사: 객체의 첫 번째 층만 복사한다. 객체 안의 객체는 참조값만 복사된다.
2. 깊은 복사: 객체 안에 있는 모든 객체까지 완전히 새로운 복사본을 만든다.
두 방식 모두 원본과는 다른 새로운 객체를 만들지만, 복사하는 깊이에 차이가 있다.
// 원본 객체
const original = {
name: "Alice",
info: {
age: 25,
city: "Seoul",
},
};
// 🔹 얕은 복사 (Shallow Copy)
const shallowCopy = { ...original };
shallowCopy.name = "Bob"; // name 변경 (원본 영향 없음)
shallowCopy.info.city = "Busan"; // 중첩 객체의 값 변경 (원본에도 영향!)
console.log(original.info.city); // "Busan" (⚠️ 원본까지 바뀜!)
console.log(shallowCopy.info.city); // "Busan"
// 🔹 깊은 복사 (Deep Copy)
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.info.city = "Daegu"; // 깊은 복사 후 수정 (원본 영향 없음)
console.log(original.info.city); // "Busan" (얕은 복사에서 변경된 상태 유지)
console.log(deepCopy.info.city); // "Daegu" (깊은 복사한 객체만 변경됨)
2-2) 참조에 의한 전달
객체를 가리키는 변수(원본)를 다른 변수(사본)에 할당하면 원본의 참조 값이 복사되어 전달되는데
이를 참조에 의한 전달이라고 한다.
즉, 두 변수가 같은 객체를 가리키고 있기 때문에, 한쪽에서 객체를 수정하면 다른 변수도 영향을 받는다.
const original = { name: '철수' };
const copy = original; // 원본의 참조 값이 복사됨
console.log(original.name); // "철수"
console.log(copy.name); // "철수"
// 한쪽에서 변경하면?
original.name = '영희';
console.log(original.name); // "영희"
console.log(copy.name); // "영희"
이처럼 원본과 사본은 저장된 메모리 주소는 다르지만, 동일한 객체를 가리키는 참조 값을 갖게 된다.
즉, 두 개의 식별자가 하나의 객체를 공유한다.
그런데 다음과 같이 두 개의 객체를 비교하면 흥미로운 결과가 나온다.
var person1 = { name: 'Lee' };
var person2 = { name: 'Lee' };
console.log(person1 === person2); // false
console.log(person1.name === person2.name); // true
이런 결과가 나오는 이유는
- person1 === person2가 false인 이유
- person1과 person2는 각각 새로운 객체.
- 객체는 메모리 공간을 따로 할당받기 때문에, person1과 person2는 다른 참조 값을 가짐.
- === 연산자는 객체의 경우 참조 값(메모리 주소)를 비교하기 때문에, 다른 객체로 인식돼서 false가 나옴.
2. person1.name === person2.name이 true인 이유:
- name 프로퍼티의 값 'Lee'는 문자열(원시 값)
- 원시 값을 비교하므로 값 자체를 비교
- 원시 값은 실제 값 자체를 비교하기 때문에, 두 값이 같으면 true가 나옴
이처럼 객체의 비교는 참조 값을 비교하기 때문에, 내용이 같더라도 다른 객체로 인식된다.
구분
|
원시 타입 (Primitive Type)
|
객체 타입 (Object Type)
|
변경 가능 여부
|
❌ 불가능 (Immutable)
|
✅ 가능 (Mutable)
|
할당 방식
|
값 자체 저장
|
참조 값(주소) 저장
|
변수 간 전달
|
값이 복사됨 (독립적)
|
참조 값이 복사됨 (같은 객체 공유)
|
복사 방식
|
그냥 복사 가능
|
얕은 복사 vs 깊은 복사 필요
|
비교 방식
|
값 자체 비교 (===)
|
참조 값(메모리 주소) 비교 (===)
|
✅ 원시 값: 안전하지만 새로운 메모리 공간이 필요함.
✅ 객체: 메모리 효율적이지만 예측 불가능한 변경 위험 존재.
'JS' 카테고리의 다른 글
모던 자바스크립트 딥다이브 13장 - 스코프 (0) | 2025.02.18 |
---|---|
모던 자바스크립트 딥다이브 12장 - 함수 (0) | 2025.02.17 |
모던 자바스크립트 딥다이브 10장 - 객체 리터럴 (1) | 2025.02.15 |
모던 자바스크립트 딥다이브 9장 - 타입 변환과 단축 평가 (1) | 2025.02.14 |
모던 자바스크립트 딥다이브 7장 - 연산자 (0) | 2025.02.12 |