변수를 선언할 때 var, let, const 키워드를 함께 사용한다.
차이점은 무엇일까?
var (함수 레벨 스코프) vs let, const (블록 레벨 스코프)
var
는 함수 레벨 스코프
다.
var로 선언된 변수의 scope는 함수로, 함수 밖에서는 이를 참조할 수 없다.
let, const
는 블록 레벨 스코프
다.
let, const로 선언된 변수의 scope는 블록(ex, {})으로, 블록 밖에서는 참조할 수 없다.
간단하게 코드로 이해해 보자.
//var a와 b를 선언해보자.
var a = "a";
{
var b = "b";
}
console.log(a); // a
console.log(b); // b
//var a, b는 모두 전역 변수로 선언되었다.
//전역 scope에서 console.log를 입력하였으므로, 변수 a,b 모두 참조 가능하다.
//이번엔 var a와 let b를 선언해보자.
var a = "a";
{
let b = "b";
}
console.log(a); // a
console.log(b); // Uncaught ReferenceError: b is not defined
//var a는 전역 변수로 a의 값이 문제 없이 나온다.
//그러나 b는 let 키워드를 사용하였기 때문에 블록 레벨 스코프의 룰을 따른다.
//console.log를 {} 블록 밖에서 입력하였기 때문에 {}안의 b는 참조할 수 없다!
var (중복 선언 허용) vs let, const (중복 선언 미허용)
var
는 중복 선언
이 허용된다
.
let, const
는 중복 선언
이 허용되지 않는다
// var는 중복선언을 하더라도 문제가 없다.
var a = "a";
var a = "aa";
console.log(a) // aa -> 마지막에 할당된 값이 나온다.
//let은 중복선언하면 SyntaxError가 나온다.
let b = "b";
let b = "bb"; //Uncaught SyntaxError: Identifier 'b' has already been declared
var, let, const (모두 호이스팅이 가능하다.)
자바스크립트는 모든 선언(var, let, const, function)
을 hoisting
한다.
그러나 var, let, const는 변수 선언 이전에 참조를 하려고 한다면, 조금씩 차이가 있다.
잠시 변수 선언/할당의 단계, 호이스팅에 대해 알아보자.
변수가 선언되고 할당되는 단계는 3단계이다.
- 변수 선언 : 변수가 선언된다. (실행 컨텍스트의 변수 객체 (Variable Object)에 등록)
- 변수 초기화 : 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 undefined로 초기화된다.
- 변수 할당 : undefined로 초기화된 변수에 입력한 값이 할당된다.
Hoisting (호이스팅)
- 변수의 선언문을 해당 스코프의 제일 상단으로 끌어올리는 현상을 말한다.
실행 컨텍스트란, 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이다.
이를 물리적으로 표현하면, 객체의 형태를 가지고 있고,
이 객체는 변수 객체(VO, Variable object), Scope Chain, thisValue 라는 3가지 프로퍼티를 가지고 있다.
함수가 실행되면, Scope Chain에 전역 스코프와 해당 함수의 스코프를 참조할 수 있도록 연결된다.
(ex, 0번째 - 전역 스코프, 1번째 - 실행한 함수 스코프)
그리고 변수 객체(VO)에 함수 내부에 선언된 지역 변수, 내부 함수 등의 정보가 담긴다.
마지막 thisValue는 함수 호출 패턴에 의해 할당되는 값이 결정된다.
(this는 함수 실행될 때 문맥, 실행 환경을 봐야한다. 어떻게 호출되었느냐에 따라 달라진다.)
실행 컨텍스트도 공부해서 정리해봐야겠다.
[출처] poiemaweb
코드로 설명해보자.
console.log(a) // undefined
var a = "a";
변수 a가 선언되기 이전에 console.log를 입력했는데, undefined가 나온다.
왜냐하면 var
는 선언과 초기화가 한번에 이뤄지기 때문이다.
변수 a는 var 키워드로 선언되었기 때문에,
변수 a가 선언되는 단계에서 undefined로 초기화되는 단계가 한번에 이뤄진다.
그런데 호이스팅 현상에 의해 var a는 scope의 상단으로 끌어올려져 있다.
그래서 변수 a가 선언/할당 되기 전에 console.log를 입력하면 undefined로 나온다.
코드는 아래와 같이 해석되었다.
var a = undefined;
console.log(a) // undefined
a = "a";
[출처] poiemaweb
코드로 설명해보자.
console.log(b)
let b = "b"; //Uncaught ReferenceError: b is not defined
변수 b가 선언되기 이전에 console.log를 입력하면, ReferenceError 가 나온다.
(참조할 수 없다는 의미!)
왜냐하면 let
은 선언과 초기화가 분리되어 진행된다.
let 키워드로 선언된 b는 선언단계와 초기화단계가 한번에 이뤄지지 않는다.
선언 단계 이후 일시적 사각지대 (TDZ, Time Dead Zone) 를 지나, 초기화가 이뤄진다.
(맞는 의미인지는 모르겠지만, 4단계로 진행된다고 생각하는게 더 이해 하기는 쉽다.)
그래서 let a가 호이스팅 현상에 의해 scope의 상단으로 끌어올려졌더라도,
undefined라는 값조차 들어있지 않기때문에 'Reference Error',
즉, '참조할 것이 없어요.' 라고 나온다.
const a
//Uncaught SyntaxError: Missing initializer in const declaration
const로 변수명만 선언하면 SyntaxError가 발생한다.
(변수 선언 조차 안된다.)
let과 const는 얼핏 보면 동일해 보인다.
그러나 const가 조금 더 엄격한? 개념이다.
let b = 'b';
b = 'bb';
// "bb"가 재할당이 된다.
const c = 'c';
c = 'cc';
//Uncaught TypeError: Assignment to constant variable.
let
은 재할당
이 가능
하지만, const
는 재할당
하면 TypeError
가 나온다.
Error Code를 보면 constant variable 이란 단어가 있는데,
const는 향후 어떠한 상황이 오더라도 재할당이 필요 없는 상수값으로 사용한다.
단, const 변수에 객체를 할당했다면, 할당된 객체의 프로퍼티는 수정, 삭제가 가능하다.
객체는 참조 타입이다. 참조 타입은 값이 할당된 메모리의 주소를 참조하고 있다.
const 변수에 담긴 객체의 프로퍼티를 변경하더라도,
const 변수에 담긴 메모리의 주소는 변경되지 않는다.
그래서 const 변수에 담긴 객체의 프로퍼티는 수정/삭제가 가능하다.
const obj = {};
obj["추가가"] = "됩니다";
console.log(obj); // {추가가: "됩니다"}
const arr = [];
arr[0] = "이것도";
arr[1] = "추가됩니다."
console.log(arr); // ["이것도", "추가됩니다."]
var, let, const 중 무엇으로 변수를 선언하는게 좋을까?
기본적으로는 const를 사용하는게 좋다.
const를 사용하면 다시 재할당도 안되고, 중복 선언도 안된다.
또 블록 레벨 스코프의 룰이 적용되어 예기치 못한 에러가 발생되는 것을 막을 수 있다.
재할당이 되어야하는 경우라면 let을 사용하면 된다.
단, 객체나 배열을 사용한다면 const를 사용하는게 좋다.
(객체의 프로퍼티도 수정, 삭제가 가능하므로!)
범위 | var | let | const |
---|---|---|---|
Scope | 함수 레벨 스코프 | 블록 레벨 스코프 | 블록 레벨 스코프 |
중복선언 | 허용 | 미허용 | 미허용 |
변수 hoisting | 적용 | 적용 | 적용 |
비고 | 변수 선언 이전 참고 가능(undefined) | 변수 선언 이전 참고 시 reference Error | 변수 선언/할당 동시! |