[JavaScript] 변수와 데이터 타입
모던 자바스크립트 Deep Dive 스터디 2회차
자바스크립트 스터디를 진행하며 해당 회차에 공부했던 내용을 직접 그림을 그리고 코드를 실행하며 저의 글로 작성합니다.
변수
변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다. 변수는 프로그래밍 언어에서 값을 저장하고 참조하는 메커니즘으로 값의 위치를 가리키는 상징적인 이름이다.
식별자
변수의 이름을 식별자라고 한다. 식별자는 특정 값을 구분할 수 있도록 구유해야 한다. 식별자는 값이 저장된 메모리 주소와 1대1 매핑 관계를 맺으며 이 매핑 관계 또한 메모리에 저장되어 있다. 즉 식별자는 메모리에 올라가 있는 값을 가지고 있는 것이 아닌 해당 메모리의 주소 값을 가지고 있는 것 이다.
변수의 선언
var keyword = "keyword";
변수의 선언은 메모리에 해당 값을 위한 공간을 확보하고 메모리의 주소와 식별자를 매핑해 값을 저장할 준비를 하는 것 이다.
변수가 선언이 되면서 값이 할당되지 않는다면 초기 값으로 undefined라는 값이 암묵적으로 할당된다.
변수의 초기화
변수의 초기화는 선언된 변수에 값을 할당하는 행위이다. 즉 변수에 매핑된 메모리의 주소 공간에 해당 값을 할당하는 것 이다.
값의 할당
변수의 선언 단계에서 메모리에 공간을 확보한다. 변수의 선언은 코드의 평가 단계에서 진행되기 때문에 암묵적으로 undefined 값이 할당된다. 그 이후 코드의 실행 단계(런타임)에서 값의 할당이 이뤄지는데, 이때 변수의 선언 당시에 확보한 메모리 공간에 값을 할당하는 것이 아닌 새로운 주소 값에 값을 할당한 후 식별자가 해당 메모리 주소를 바라보게 된다.
값의 재할당
선언된 변수에 값을 재할당 한다면 해당 메모리 공간에 값을 변경하는 것이 아닌 다른 메모리 공간을 확보한 후 해당 메모리에 값을 할당, 해당 변수가 새롭게 확보된 공간을 바라보게 된다.
위 메모리를 살펴보면 최종적으로 12, 24번 주소의 메모리는 사용하고 있지 않다는 것을 확인할 수 있다. 해당 값들은 자바스크립트의 GC (가비지 컬렉터) 에 의해서 회수된다.
호이스팅
자바스크립트의 실행은 코드의 평가 단계와 실행 단계로 나눠진다. 자바스크립트가 실행되면 자바스크립트 엔진은 작성된 코드를 평가한다. 이 단계에서 코드를 위에서 아래로 읽어나가며 선언된 변수, 함수를 실행한다.
console.log(keyword); //undefined
var keyword = "keyword";
console.log(keyword); //keyword
위 예제 코드를 살펴보면 첫 번째 로그로 undefined가 출력된다. 자바스크립트가 실행이 되면서 코드의 평가 단계에서 keyword라는 변수의 선언을 실행하기 때문이다. 평가단계에서 변수가 선언이 되었으니 keyword라는 변수를 참조할 수 있고, 암묵적으로 바인딩 된 undefined를 출력한다. 그 이후에 실행 단계(런타임)에서 keyword의 값이 “keyword” 라는 리터럴로 바인딩이 되어 5번째 행에서 “keyword” 를 출력하게 된다.
첫 번째 행에서 keyword는 선언된 변수가 아니지만 참조가 가능하다. 코드의 선언문이 작성된 코드의 최 상단으로 끌어 올려진 것 처럼 동작한다. 이를 두고 자바스크립트에서는 호이스팅(끌어올림)이라고 부르고 있다.
var 키워드로 선언된 변수 뿐만 아니라 let, const, fuction, class 등의 키워드로 선언된 모든 식별자는 호이스팅 된다. var 키워드는 코드의 평가 단계에서 식별자의 선언과 암묵적 초기화가 동시에 진행된다. 이때 초기화는 개발자가 작성한 할당문이 실행되는 것이 아닌 undefined가 암묵적으로 바인딩 되는 것이다.
let 키워드로 선언된 변수 역시 호이스팅이 되지만 var 키워드와 다르게 동작한다. let 변수는 코드의 평가 단계에서 변수 선언만 발생한다. 변수의 초기화 단계는 코드가 실행될 때 초기화 문을 만나면 진행된다.
let, const 키워드로 선언한 변수는 호이스팅은 발생하지만 선언되기 이전에 참조할 수 없다. 이는 해당 키워드로 선언한 변수는 일시적 사각지대 (TDZ)에 의해 관리되기 때문에 변수가 선언되기 이전에 참조할 수 없다.
데이터의 타입
자바스크립트의 데이터 타입은 원시 타입과 객체 타입으로 분류한다. 원시 타입은 불변한 값이며 객체 타입은 변할수 있는 값으로 분류한다. 원시 타입은 숫자, 문자열, 템플릿 리터럴, undefined, null, 심벌 타입으로 총 6가지이다.
데이터의 타입이 필요한 이유는 다음과 같다.
- 값을 저장할 때 메모리 공간의 크기를 결정하기 위해
- 값을 참조할 때 한 번 읽어 들어야 할 메모리 공간의 크기를 결정하기 위해
- 메모리에서 읽어 들인 2진수를 어떻게 해석할 지 결정하기 위해
자바스크립트에서 데이터의 타입은 런타임에 결정이 된다. 코드의 작성 시점에 타입을 지정하지 않고 런타임 시 선언된 변수에 값이 할당되는 순간에 타입이 결정(타입 추론)된다.
선언된 변수는 언제든지 다른 타입의 값으로 재할당 될 수 있다. 이는 동적으로 타입이 정해진다는 것 이며 이러한 특성으로 인해 자바스크립트를 동적 타입 언어 라고 부른다.
타입이 동적으로 정해지는 언어는 한번 선언한 변수에 다양한 값을 할당할 수 있지만 이로 인해서 타입 안정성이 떨어짐으로 소프트웨어에 대한 신뢰성이 떨어진다. 또한 타입을 단언하기 위해 여러 방어 코드를 작성해야 함으로 정적 타입 언어에 비해 가독성이 떨어질 수 있다. 아래의 예시 코드는 React에서 타입을 단언 (방어) 하기 위한 코드이다.
const Component = (props) => {
const handleClick = () => {
if (props.onClick instanceof Function) {
props.onClick();
}
}
const label = props?.label ?? '버튼';
const count = typeof props?.count === 'number' ? props.count : 0;
return <button>{label}</button>
}
props로 전달받는 onClick, label, count 값을 런타임 에러를 피하기 위해 다양한 방어 코드를 작성하고 있다.