자바스크립트의 데이터 타입은 number, string, boolean, undefined, null, symbol 6가지 primitive type과 reference type이 존재한다. 원시값은 immutable하며 원시 값을 할당한 변수에 재할당하면 새로운 메모리 공간을 확보하여 저장한다. 이와 달리 객체값은 mutable하며 객체를 직접 변경할 수 있다. 객체는 크기가 정해져 있지 않기 때문에 메모리 효율성을 위해 재할당하지 않는다. 원시값과 참조값의 메모리 할당 방식을 이해하며 얕은 복사와 깊은 복사의 차이를 알 수 있다.
원시 타입(primitive type)
: number, string, boolean, undefined, null, symbol (ES11 : BigInt 추가)
객체 타입(reference type)
: 객체, 함수, 배열 등
모든 수를 실수로 처리
하며, 정수만 표현하기 위한 데이터 타입은 없음
0으로 나눌 경우 : Infinity
, -0으로 나눌 경우 : -Infinity
, 연산이 불가능한 경우 : NaN
키워드나 식별자 같은 토큰과 구분
하기 위해 작은 따옴표를 사용하여 감싼다
undefined 타입의 값은 undefined 가 유일
var 키워드로 선언한 변수는 암묵적으로 undefined로 초기화
개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화할 때 사용하는 값
변수에 값이 없다는 것을 명시하고 싶을 때는 null
을 할당
- 변수에 할당되는 값의 데이터 타입에 따라
확보해야 할 메모리 공간의 크기
결정- 값을 참조할 때
한 번에 읽어 들여야 할 메모리 공간의 크기
결정- 메모리에서 읽어들인
2진수를 해석할 방법
결정
정적 타입 언어는 변수의 타입을 변경할 수 없으며, 컴파일 시점에 타입 체크 수행
JS는 동적 타입 언어로 변수 선언 시 타입 선언 ❌
, 타입에 상관없이 자유롭게 할당
선언이 아닌 할당에 의해 타입 결정 : 타입 추론
재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있음 : 동적 타이핑
변수는 타입을 갖지 않지만, 값은 타입을 갖는다
→ 현재 변수에 할당되어 있는 값에 의해 변수의 타입이 동적으로 결정된다
- 변수는 꼭 필요한 경우에 한해 제한적으로 사용하기
- 변수의 개수가 많을수록 타입을 잘못 추론할 확률이 높아지므로 오류발생 확률도 높음
- 변수의 스코프는 최대한 좁게 만들어 변수의 부작용 억제
- 전역 변수 지양
- 의도치 않게 값이 변경될 확률 높음
- 다른 코드에 영향을 줄 확률 높음
- 변수보다는 상수를 사용해 값의 변경 지양
- 변수 이름은 변수의 목적이나 의미를 파악할 수 있도록 네이밍
- 식별자의 스코프가 넓을수록 명확한 이름을 명명하도록 노력
원시 값 : 변경 불가능한 값(immutable), 변수에 실제 값 저장, pass by value
원시 값은 변경 불가능하다 == 원시 값 자체를 변경할 수 없다 ≠ 변수 값을 변경할 수 없다
let primitive = 80;
primitive = 100; // OK
원시 값 자체와, 원시값을 할당한 변수를 헷갈리면 안된다
재할당하면 서로 다른 메모리 공간에 저장
⇒ 변수에 할당된 원시 값이 변경 불가능한 값이기 때문
[ 변수 값을 변경하는 과정 ]
1. 변수 값을 변경하기 위해 원시 값을 재할당
2. 새로운 메모리 공간을 확보하여 재할당한 값 저장
3. 변수가 참조하던 메모리 공간의 주소 변경
불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법 없음
재할당 이외에 변수 값을 변경할 수 있다면 상태 변경을 추적하기 어려움
문자열은 0개 이상의 문자로 이루어진 집합이며, 1개의 문자는 2바이트의 메모리 공간에 저장
문자 개수에 따라 필요한 메모리 공간의 크기 결정
문자열은 유사 배열 객체이면서 이터러블
[ 유사 배열 객체 ]
배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고, length 프로퍼티를 갖는 객체
원시 값을 객체처럼 사용하면 원시값을 감싸는 래퍼 객체로 자동 변환
"변수에 변수를 할당했을 때 무엇이 어떻게 전달되는가"
변수에 원시 값을 갖는 변수를 할당하면 값이 복사되어 전달
두 변수는 같은 값을 갖지만 다른 메모리 공간에 저장
var score = 80;
var copy = score;
score = 100;
console.log(score); // 100
console.log(copy): // 80
[ JS 엔진을 구현하는 제조사에 따라 실제 내부 동작 방식은 차이 존재 ]
1. 원시 값을 갖는 변수를 할당할 때부터 다른 메모리 공간에 저장
2. 원시 값을 갖는 변수를 할당하는 시점에는 같은 원시값을 참조하다가 재할당이 이뤄질 때 새로운 메모리 공간 할당
객체 값 : 변경 가능한 값(mutable), 변수에 참조 값 저장, pass by reference
객체는 크기가 정해져있지 않으므로, 원시 값과 같이 확보해야 할 메모리 공간의 크기를 정해둘 수 없다
[ 자바스크립트 객체 관리 방식 ]
JS엔진은 해시 테이블보다 나은 방법으로 객체 구현
클래스 없이 객체를 생성할 수 있으며, 객체가 생성된 이후에도 동적으로 프로퍼티와 메서드 추가 가능
→ 클래스 기반 언어의 객체보다 생성과 프로퍼티 접근에 비용이 더 많이 듦
V8 JS 엔진에서는 프로퍼티에 접근하기 위해 동적 탐색 대신히든 클래스
방식 사용
→ C++ 객체의 프로퍼티에 접근하는 정도의 성능 보장
→ 자바와 같이 고정된 객체 레이아웃(클래스)과 유사하게 동작
객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조값에 접근 가능
객체를 할당한 변수에는 생성된 객체가 실제로 저장된 메모리 공간의 주소
저장
객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있음
객체를 할당한 변수에 재할당 하지 않았으므로 객체를 할당한 변수의 참조 값은 변경되지 않음
객체를 변경할 때마다 원시 값처럼 새롭게 생성한다면 메모리 사용이 비효율적이며 성능 저하
→ 메모리 사용의 효율성과 성능을 위해 어느 정도의 구조적인 단점을 감안한 설계
원시 값과는 다르게 여러 개의 식별자가 하나의 객체를 공유할 수 있다
얕은 복사
: 객체를 한 단계까지만 복사하는 것 (또는 참조값을 공유하는 것)
깊은 복사
: 객체에 중첩되어 있는 객체까지 모두 복사하는 것 (또는 서로 다른 메모리 공간에 저장하는 것)
얕은 복사와 깊은 복사로 생성된 객체 모두 원본과는 다른 객체
객체에 중첩되어 있는 객체의 참조값은 얕은 복사의 경우 그대로고, 깊은 복사의 경우 구분된다.