Chapter 11 원시값과 객체의 비교
Primitive Values and Objects
1회독
11.1 원시값
원시 타입 | 객체 타입 |
---|---|
primitive type | object/ref type |
immutable (read-only) | mutable |
확보된 메모리 공간에 실제 값 저장 (변수에) | 메모리의 참조값 저장 |
원본의 원시 값 복사 (pass by value) | 참조값 복사 (pass by reference) |
Immutability 불변성
- 값 변경불가능이 아니라, 값에 대한 진술이 변경 불가능
- 변경법 = 실제 변경이 아닌 교체
- 단, 상수는 교체 금지 (상수 = 재할당 금지된 변수)
- 불변하기에 데이터 신뢰성 보장
문자열
📍 FUN FACT
ECMAScript 사양에 문자열 타입 (2B), 숫자타입(8B) 이외 원시 타입 크기를 명확히 규정 X, 브라우저 제조사의 구현에 따라 원시 타입의 크기가 다를 수 있다!!
- 숫자는 1이던 10000이던 동일한 8B 차지
문자열은 길이에 따라 크기 다름 (2B per character)
- 때문에
C
: 문자를 위한 데이터 타입 (char)만 존재하지, 문자열 타입은 XX Java
: 문자열을String
객체로 다룸JavaScript
: 문자열을 원시 타입으로 다룸 (for 개발자 편의성)
- 때문에
- 문자열 aka 유사배열객체 (Array-like Object)
- 원시 값을 객체처럼 사용 시 원시값을 감싸는 래퍼 객체로 자동 변환
- \(\rightarrow\) 문자열은 배열처럼 인덱스로 접근 가능
- \(\rightarrow\)
length
프로퍼티 존재 - \(\rightarrow\)
for...of
사용 가능
값의 의한 전달 (Pass by Value)
- 원시값은 값: 값 복사되어 다른 메모리에 저장됨 (변수에 변수값 전달 시)
- 독립적, 서로 간섭 불가하기 때문에 한쪽 변경해도 다른쪽 영향 X
📍 FUN FACT
Python: 변수에 변수값 전달 시 두 변수가 같은 메모리 주소 참조. 추후 한쪽 변경 시 새로운 메모리 값에 재할당된 값 저장
📍 FUN FACT
사실 ‘값에 의한 전달’은 ECMAScript 사양에 없는 용어. ‘값에 의한 전달’은 ‘공유에 의한 전달’이 더 옳은 표현이긴 하다
11.2 객체
- prop 개수 제한 X
- 동적으로 추가/삭제 가능
- prop 값에 제약 X
\(rightarrow\) 값이 매우 클 수도 있음. \(\rightarrow\) 생성, 프롭 접근 시 비용 많이 듦
객체 관리 방식
- \(\approx\) 해시 테이블과 유사, but better
- Class OOP (
Java
,C++
): 객체 생성 시 클래스 정의 필요 (프롭, 메서드 이미 정해져 있음) Prototype-based OOP (
JavaScript
): 동적으로 생성추가 때문에 비용 많이 듦- Dynamic Lookup 대신 Hidden Class 방식 사용해 거의 C++ 급의 성능 보장
- 📌 TODO: Chrome v8 JS Engine 객체 관리 방법 찾아보기
변경 가능한 값 (Mutable)
변수명 = 객체의 참조값 저장
- so,
값을 갖는다
가 아니라객체를 참조한다
고 표현 - 재할당 없이 직접 객체 변경 가능
- 재할당 하지 않았으므로 객체 할당한 변수의 참조값 변경 X
- so,
변경가능한 값으로 설계된 이유:
- JS 객체는 유연하기 때문에 (동적으로 프로퍼티 추가/삭제 가능) deep copy 시 비용 너무 많이 듦 (무한대 가능)
얕은 복사 vs 깊은 복사
var obj = { a: 1, b: { c: 2 } }; var shallowCopy = { ...obj }; // 얕은 복사 var deepCopy = _.cloneDeep(obj); // lodash 사용 console.log(obj === shallowCopy); // false console.log(obj.b === shallowCopy.b); // true console.log(obj === deepCopy); // false console.log(obj.b === deepCopy.b); // false
- 얕은 복사: object 한 단계까지만 복사
- 깊은 복사: object 내부까지 모두 복사
- 원시값 복사도 깊은 복사로 볼 수 있음
⚠️ 부작용: 여러개의 식별자가 하나의 객체 공유 가능
- \(\rightarrow\) 한 식별자로 객체 변경 시 다른 애한테도 영향 미침
2회독
불변성
- 상수 <-> 변수 이렇게 생각할 수 있지만, 사실 상수는 재할당이 금지된 변수이다.
원시값은 immutable | (만약에 mutable 하다면) |
---|---|
![]() | ![]() |
불면성 유지 -> 신뢰도 높음, 상태 변경 추적 수월!!
그럼 문자열은 어떻게 변경되는가?
let str = "HELLO"; str = "WORLD"; console.log(str); // WORLD, 사실 재할당된거임. 주소 변경이지 실제 값 변경 X str[0] = "W"; // ❌ 에러발생도 안되고 그냥 무시~ console.log(str); // HELLO
- 예기치 않은 변경으로부터 자유롭다
위 Primitive 문자열에 대해 어떻게 인덱싱이 가능한가?
- => 문자열은 유사객체배열로 래핑됨 (Array-like Object)
- 인덱싱, length 프로퍼티 사용 가능, 이터러블 하기에
for...of
사용 가능 ⁉️ 언제 래핑되는가?
사용 (접근) 시 임시로 래핑된다고 한다 (Auto-Boxing, implicit boxing) 프로퍼티/메서드 접근이 끝나면 래퍼 객체는 GC 대상이 됨 ㅠㅠ
값에 의한 전달, 참조에 의한 전달
- 애초에 둘 다 JS 용어가 아님
값에 의한 전달 (원시값)
const score = 80;
const copy = score;
score = 90;
console.log(score, copy); // 90 80
서로 간섭 불가능 (따로 논다)
어떻게 복사되는가? 두 가지 가설이 있음 (ECMAScript에 딱히 안나와있지만, 유추해볼 수 있음)
쌩 별개로 복사됨 | 일단 두 식별자가 같은 값 참조 |
---|---|
![]() | ![]() |
복사 시 애초에 다른 메모리 주소에 할당됨 | 복사 시 일단 두 식별자가 같은 값 참조하고 재할당 시 별개로 복사됨 (ex: 파이썬) |
- 둘 중 어떤 것이던 간에 서로 간섭 안하는 것은 맞음.
사진에서 보이다시피,
값에 의한 전달
도 사실은 메모리 주소를 전달함 (값이 아니라!).- 대신 요 메모리 주소로 접근하면 실제 값 참조 가능
- ⁉️ 둘 중 뭐임???
참조에 의한 전달 (객체)
서로 영향을 미침
객체는 동적으로 프로퍼티 추가/삭제 가능하기에 확보해야할 메몸리 공간의 크기를 사전에 정할 수가 없음 ㅠㅠ
- JS 의 객체가 다른 언어의 객체와 다른 점: 1) 클래스 없이도 객체 생성 가능 2) 프로퍼티 동적 추가/삭제 가능
- 따라서 편리하지만 메모리 관리 어려움, 비효율, 위험함
- 많이 비효율적이라 생각하겠지만 사실 hidden class라는 기법 사용해서 거의 C++ 급의 성능 보장
메모리에 저장된 객체를 실제로 직접 수정 가능함
사진으로 설명
const person = { name: "Park" }; person.name = "Kim"; // 값 변경 person.address = "Seoul"; // 프로퍼티 동적 생성
위 단점: 여러 식별자가 하나의 객체 참조 가능 -> 서로 마구마구 변경 시 예기치 않은 결과 발생
얕은 복사, 깊은 복사
- 얕은 복사 (shallow copy): 1-depth 까지만 복사, (또는 객체 복사 할때 사용되는 용어이기도 함)
- 깊은 복사 (deep copy): 모든 레벨의 객체 복사, (또는 원시값 복사 할때 사용되는 용어이기도 함)
결국 “값에 의한 전달”과 “참조에 의한 전달”은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일하다. 다만 식별자가 기억하는 메모리 공간, 즉 변수에 저장되어 있는 값이 원시 값이냐 참조 값이냐의 차이만 있을 뿐이다. 따라서 자바스크립트에는 “참조에 의한 전달”은 존재하지 않고 “값에 의한 전달”만이 존재한다고 말할 수 있다.
이 문장을 이해하기 위해서는 다음과 같은 핵심 개념들을 살펴봐야 합니다:
- 모든 전달의 본질:
- JavaScript에서 모든 데이터 전달은 실제로 “값의 복사”입니다
- 원시값이든 객체든, 변수가 가지고 있는 값을 복사해서 전달합니다
- 변수에 저장되는 것:
- 원시값의 경우: 실제 값이 저장
- 객체의 경우: 참조값(메모리 주소)이 저장
- 전달되는 것:
- 원시값의 경우: 실제 값이 복사되어 전달
- 객체의 경우: 참조값(메모리 주소)이 복사되어 전달
따라서 “참조에 의한 전달”이라고 부르는 것도 실제로는 참조”값”을 복사해서 전달하는 것이므로, 본질적으로는 “값에 의한 전달”입니다.