2021-06-08
자바스크립트를 배우는 사람이라면, 변수가 무엇인지부터 배울 것이다. 영어로는 variable이라고 불린다. 이런 변수가 무엇이고, 어떻게 사용하는지, 키워드에 따라 어떻게 다른지 자세히 알아보자.
우선, 변수란 값을 저장하기 위해서 확보한 메모리공간을 구별하기 위해서 붙인 이름표라고 생각하면 쉽다. 이 이름표를 붙인 저장공간인 변수에 값을 저장하는 것을 할당, 변수에 저장한 값을 불러들이는 것은 참조, 변수명을 자바스크립트 엔진에 알리는 것을 선언이라고 한다. 그런데 찾아보며 '변수 초기화'라는 것도 있다는 것을 쓰니는 처음 알게 되었다.(두둥) 찾아보며, 대부분 할당과 초기화를 같이 표현하고 있어서 감을 잡기가 어려웠는데 구분해서 정의해보자면 이렇다. 할당은 변수자체에 값을 저장하는 것이라면, 초기화는 선언한 변수에 처음으로 값을 저장하는 과정이라고 보면 된다.
아래 예시를 보자.
//1번
var wanny; //변수 선언만
//2번
var wanny = 0; //변수 선언+초기화
1번을 먼저 봐보자. 우리가 알고 있는 var키워드를 사용했다. 1번처럼 사용하는 것은 var키워드를 사용해 변수를 선언하기만 한 것이다. 여기서 1번은 변수에 값을 넣어주지 않았기 때문에, 변수는 값이 설정될 때까지 undefined 값을 갖게 된다. 2번은 var키워드를 사용해 변수를 선언해주었고, 0을 할당해주었다. wanny에 0을 넣어준 것이다. 자 이 때, 변수 초기화도 같이 실행이 된 것이다. 선언한 wanny라는 변수에 0이라는 값을 처음으로 저장했기 때문이다.
그렇다면, var, let, const 키워드 별로 어떻게 다른지도 한번 알아보자!
var,let,const의 차이점을 구체적으로 5가지로 나누어서 생각해볼 수 있겠다.
- 중복선언가능여부
- 재할당가능여부
- Scope
- Hoisting
- 전역객체 프로퍼티여부
위의 5가지로 나누어 var,let,const가 어떻게 다른지 구체적으로 알아보자!
var는 중복선언이 가능하지만, let,const는 불가능하다
var a = 1; console.log(a); // 1
// 두번째 변수 선언+초기화
var a = 2; console.log(a); // 2
// 세번째 변수 선언(초기화X)
var a; console.log(a); // 2
// let 중복 선언
let a = 10; let a = 20;
// SyntaxError: Identifier 'a' has already been declared
// const 중복 선언
const b = 10; const b = 20;
// SyntaxError: Identifier 'b' has already been declared
위의 예제를 보면, var키워드를 사용해 중복해서 변수선언과 초기화가 가능하다. 단, 마지막 할당이 변수에 저장된다. 이때, 선언만 했을경우 선언문자체가 무시된다. 반면 let,const는 선언한변수를 다시 선언하면 syntaxerror가 발생하기 때문에 중복선언은 불가능하다.
var와 let은 재할당이 가능하지만, const는 불가능하다.
var a = 10;
a = 20;
console.log(a); // 20
let b = 111;
b = 222;
console.log(b); // 222
const c = 111;
c = 222; // TypeError: Assignment to constant variable.
const a = 10;
const b; // SyntaxError: Missing initializer in const declaration
위의 예시를 보면, var와let은 변수선언 및 초기화 이후에 반복해서 다른값을 재할당할 수 있는 반면, const는 상수 선언키워드이며, 재할당 할 수 없고, 처음 선언할때 반드시 값 할당을 해주어야한다.
var는 function-level scope, let,const는 block-level scope이다.
먼저, 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다. 블록레벨스코프는 코드 블록({…})내에서 유효한 스코프를 의미한다. 여기서 유효하다라는 것은 참조(접근)할 수 있다는 뜻이다.
function hello(){
var a = 10;
console.log(a);
}
hello(); // 10
console.log(a); //ReferenceError: a is not defined
if(true) {
var a = 10;
console.log(a); // 10
}
console.log(a); // 10
if(true) {
let a = 10;
console.log(a); // 10
}
console.log(a); // ReferenceError: a is not defined
위의 첫번째 예제처럼 var는 함수내부에 선언된 변수만 지역변수로 한정하고 나머지는 전역변수이다. 함수가 아닌 영역에서는 전역변수로 취급된다. 위 예제는 block level scope이므로 전역변수로 취급된다. let을 블록레벨스코프이므로 외부에서 참조되지 않으며, 함수도 {}으로 이루어지기 때문에 함수도 포함이 된다.
var는 let,const는 변수호이스팅이 발생하지만, let,const는 다른방식으로 작동한다.
console.log(a); // undefined
var a = 10;
console.log(a); // 10
-> 뒤에서 선언된 변수 a가 앞에서 참조되었음에도 에러를 발생시키지 않는다.
코드 실행 전에 자바스크립트 엔진이 미리 변수를 선언하고, undefined로 초기화해 두었기 때문이다.
console.log(a); // ReferenceError: a is not defined
let a = 10;
-> 뒤에서 선언된 변수를 앞에서 참조하려 하니 에러가 발생한다. let, const로 변수를 선언하는 경우, 코드 실행 전에는 변수 선언만 해두며, 초기화는 코드 실행 과정에서 변수 선언문을 만났을 때 수행한다. 그래서 호이스팅이 발생하기는 하지만, 값을 참조할 수 없어서 호이스팅이 발생하지 않는 것처럼 보이는 것이다. 이 때, 변수의 선언과 초기화 사이에 일시적으로 변수 값을 참조할 수 없는 구간을 TDZ(Temporal Dead Zone)라고 한다.
그렇다면, 호이스팅이 발생하는걸 어떻게 확인할 수 있을까?
아래의 두 코드를 비교해보면 알 수 있다.
let a = 10; // 전역변수 a선언
if(true){
console.log(a); // 10
}
위 코드는 전역변수로 선언된 a의 값 10을 if문 블럭에서 참조하여 출력하고 있다.
let a = 10; // 전역변수 a선언
if(true){
console.log(a); // ReferenceError: a is not defined
let a = 20; // 지역변수 a 선언
}
이 코드는 if문 블럭 내부에서 지역변수 a를 다시 선언했다. 지역변수가 전역변수보다 우선순위를 가지는데 이 경우, 지역변수 a 앞에서 console.log()로 참조시 전역변수 a가 있어도 에러가 발생한다.왜냐하면 지역변수 a가 호이스팅되면서 TDZ 구간이 만들어졌기 때문이다.즉, let으로 선언된 변수도 호이스팅이 발생함을 알 수 있다.
var로 선언된 변수는 전역객체(window)프로퍼티지만, let,const는 아니다.
크롬콘솔에서 코드를실행하면 var로 선언된 변수는 window 프로퍼티로 할당되는 반면에 let,const는 window프로퍼티에 할당되지 않음을 알 수 있다.(let a=10으로 선언해줬다고 했을때 window.a는 undefined)