변수와 타입
- 2.1 변수란 무엇인가?
- 2.2 식별자와 호이스팅
- 2.3 값의 할당과 재할당
- 2.4 값, 리터럴, 표현식, 문
- 2.5 데이터 타입
- 2.6 동적 타이핑
- 2.7 연산자
- 2.8 제어문
- 2.9 타입 변환과 단축 평가
변수, 타입, 연산자, 제어문, 타입 변환 — JS 코드를 읽고 쓰기 위한 기본 문법을 한꺼번에 잡아두자.
변수는 단순히 "값을 담는 그릇"이 아니야. 하나의 값을 저장하기 위해 확보한 메모리 공간, 또는 그 공간을 식별하기 위한 이름이 변수거든. 10 + 20을 계산하면 결과값 30이 메모리 어딘가에 저장되는데, 메모리 주소를 직접 다루는 건 위험하니까 변수라는 이름표를 붙여서 접근하는 거지. 변수 이름을 **식별자(identifier)**라고 하는데, 식별자는 값이 아니라 메모리 주소를 기억해. 변수뿐 아니라 함수, 클래스 이름도 모두 식별자야.
var, let, const 키워드로 변수를 선언하면 JS 엔진이 두 단계를 거쳐. 먼저 선언 단계에서 변수 이름을 실행 컨텍스트에 등록하고, 초기화 단계에서 메모리 공간을 확보하고 undefined로 초기화하지. var는 선언과 초기화가 동시에 일어나. 변수 선언이 코드의 선두로 끌어올려진 것처럼 동작하는 것을 **호이스팅(hoisting)**이라 하는데, JS 엔진이 소스코드 실행 전에 평가 단계에서 모든 선언문을 먼저 처리하기 때문이야.
console.log(score); // undefined (에러가 아님!)
var score;
선언과 할당은 다른 시점에 실행돼. 선언은 런타임 이전(평가 단계), 할당은 런타임에 순차적으로. var와 let은 재할당이 가능하고, const는 재할당이 불가능해. 재할당하면 기존 메모리 공간의 값을 지우는 게 아니라, 새로운 메모리 공간에 값을 저장하고 변수가 그쪽을 가리키게 돼. 이전 값은 가비지 컬렉터가 나중에 정리하지.
표현식과 문의 구분도 확실히 잡아야 해. **값(value)**은 식이 평가되어 생성된 결과야. **리터럴(literal)**은 사람이 이해할 수 있는 문자로 값을 생성하는 표기법이고 — 3, 'hello', true, null, {}, [] 같은 것들. **표현식(expression)**은 값으로 평가될 수 있는 문이고, **문(statement)**은 프로그램을 구성하는 기본 단위이자 최소 실행 단위야. 이 구분이 실질적으로 중요한 게 뭐냐면, 표현식인 문은 값으로 평가되니까 변수에 할당할 수 있고, 표현식이 아닌 문은 할당할 수 없어.
var x = var y; // SyntaxError — var 선언문은 표현식이 아닌 문
var x = 100; // OK — 100은 표현식
JS에는 원시 타입 6개 + 객체 타입 1개가 있어. 숫자는 정수든 실수든 모두 64비트 부동소수점 하나로 처리해서 1 === 1.0이 true이고, 0.1 + 0.2 !== 0.3 같은 부동소수점 이슈가 존재하지. undefined vs null 구분도 중요한데, undefined는 JS 엔진이 자동으로 넣는 값이고, null은 개발자가 의도적으로 비어있음을 표현할 때 쓰는 거야. **심벌(symbol)**은 ES6에서 추가된 유일무이한 값이고, 템플릿 리터럴은 백틱으로 감싸면 ${expression}으로 표현식을 삽입할 수 있어.
타입이 왜 필요하냐면, 값을 저장할 때 확보해야 할 메모리 공간의 크기를 결정하고, 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해서야. JS는 동적 타입 언어라서 변수를 선언할 때 타입을 지정하지 않고, 할당되는 값에 따라 타입이 런타임에 결정돼. 유연하지만, 타입의 변화를 추적하기 어렵다는 단점이 있지.
연산자에서 진짜 중요한 건 == 대신 ===를 쓰라는 거야. ==는 타입이 다르면 암묵적 타입 변환 후 비교하고, ===는 타입까지 같아야 true를 반환하거든. 그리고 typeof null이 'object'를 반환하는 건 JS 초창기부터의 버그야. null 체크는 === null로 해야 해. 삼항 조건 연산자는 if문과 달리 표현식이라 값으로 사용할 수 있고, **지수 연산자(**)**는 ES7에서 추가됐어.
제어문은 기본 중의 기본이지만, 모던 JS에서는 고차 함수로 대체하는 경향이 있어. switch의 fall through(break 빼먹으면 다음 case로 쭉 이어지는 것)를 조심하고, continue를 잘 쓰면 들여쓰기를 줄여서 가독성을 높일 수 있지.
타입 변환은 JS에서 가장 혼란을 일으키는 주제 중 하나야. + 연산자는 피연산자 중 하나가 문자열이면 문자열 연결을 하고, 나머지 산술 연산자는 숫자로 변환하는 비대칭 동작이 있거든. Falsy 값 6가지(false, undefined, null, 0, NaN, '')는 반드시 외워야 해.
실무에서 진짜 많이 쓰이는 건 단축 평가야. &&와 ||는 불리언이 아니라 피연산자 중 하나를 반환해. 모던 JS에서는 && 대신 **옵셔널 체이닝(?.)**을, || 대신 **null 병합 연산자(??)**를 쓰는 게 안전해. ?.는 오직 null/undefined만 체크하고, ??는 0이나 빈 문자열을 기본값으로 대체하지 않거든.
'Cat' && 'Dog' // 'Dog' (첫 번째가 truthy이면 두 번째 반환)
'Cat' || 'Dog' // 'Cat' (첫 번째가 truthy이면 첫 번째 반환)
obj?.prop // obj가 null/undefined면 undefined, 아니면 prop 접근
value ?? '기본값' // value가 null/undefined일 때만 기본값
정리
2장 읽고 기억할 거 세 가지:
- 변수의 핵심은 메모리 관리의 추상화이고, 호이스팅은 JS 엔진의 2단계 처리(평가 → 실행) 때문에 발생해.
- 동적 타이핑은 유연하지만 위험해.
===를 쓰고, Falsy 값 6가지를 외우고,?.와??를 활용하자. - 표현식은 값을 만들고, 문은 행동을 해. 이 구분이 삼항 연산자 vs if문, 화살표 함수의 암묵적 반환 등을 이해하는 기반이야.