변수 Hoisting 과 var , let , const 키워드
Hoisting이란 ?
변수 선언문이 코드의 맨 위로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징. 스코프 단위로 동작한다.
console.log(item); // undefined
var item; // 변수 선언문
console.log가 실행되는 시점에 , item 변수는 선언되어있지 않았으므로 에러가 뜰 것 같지만 , 실제로는 에러가 발생하지 않고 undefined가 실행된다. 변수 선언은 소스코드가 실행되는 시점이 아니라 , 그 이전 단계에서 이미 실행되기 때문이다.
console.log(item); // undefined
var item; // 변수 선언문
item = 10; // 값 할당
console.log(item); // 10
다만 할당은 이야기가 다르다. 할당은 소스코드가 실행이 되는 시점에 실행된다. 그래서 위와 아래의 console.log의 값이 다른 것이다.
console.log(item); // undefined
var item = 10; // 변수 선언문 + 할당
console.log(item); // 10
위 경우는 어떨까 ?? 선언문과 할당이 같이 있는 경우이다. 이 경우에는 선언되면서 할당되기 때문에 모두 10이 나올 것 같지만 , 실제로 자바스크립트는 선언과 할당을 나누어서 진행한다. 그래서 선언이 되고 undefined로 초기화가 된 후에 특정 소스코드가 실행 될 때 10이 할당이 되는 것이다.
console.log(item);
item = 10;
var item;
console.log(item);
그렇다면 이 경우에는 어떨까 ?? 선언이 아래 있을 뿐만 아니라 할당마저 선언보다 먼저 있다. 이 경우도 마찬가지로 위는 undefined, 아래는 10이 나오게 된다. 마찬가지로 선언이 맨 위로 끌어올려져서 실행이 되고 , undefined로 초기화 된 후에 소스코드를 만나면 10이 할당이 된다. 그 이후 아래 출력이 이루어진다.
var
ES5 에서는 변수를 선언할 방법이 유일하게 var 뿐이었다.
var의 특징
1. 중복 선언 가능
var x = 1;
var y = 1;
var x = 100;
var y;
console.log(x); // 100
console.log(y); // 1
x 변수와 y 변수가 모두 중복 선언되었다. 이때 , 에러가 발생하지 않는다. 그래서 동일한 이름이 있다는 것을 모르고 중복 선언한 경우 의도치 않게 먼저 선언된 변수의 값이 변경되는 사태가 발생할 수 있다.
2. 함수 레벨 스코프
var 키워드는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다. 즉 , 함수 외부에서 var 키워드를 선언하면, 코드 블록 내에서 선언해도 모두 전역변수가 되어버린다.
var x = 1;
if(true) {
var x = 10;
}
console.log(x); // 10
--------------------------------------------------
var i= 10;
for(var i=0; i<5; i++){
console.log(i); // 0 1 2 3 4
}
console.log(i); // 5
var의 Hoisting
맨 위 Hoisting을 설명할 때 다루었다. 다만 , ES6 에 와서는 let과 const가 추가되었다. 이 때의 경우는 어떤식으로 달라지는지 확인해 보겠다.
let
ES6에서 새롭게 추가된 변수 선언 키워드이다. var과의 차이점을 중심으로 비교해보겠다.
1. 변수 중복 선언 금지
var에서는 중복으로 선언해도 에러가 나지 않았지만 , let에서는 이름이 같은 변수를 중복 선언하면 에러가 발생한다.
var foo = 123;
var foo = 456; // 문제없음
let boo = 123;
let boo = 456; // 문제 발생
console.log(window.boo); // undefined
또한 , let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. 그래서 window.boo 와 같이 접근할 수 없다.
2. 블록 레벨 스코프
var은 함수 레벨 스코프를 따르지만 , let은 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
let foo = 1; // 전역 변수
{
let foo = 2; // 지역 변수
let bar = 3; // 지역 변수
}
console.log(foo); // 1
console.log(bar); // bar is not defined
let 키워드는 블록 레벨 스코프를 따르므로 , 코드 블록 내의 선언된 foo 변수와 bar 변수는 지역변수이다. 그래서 전역에서는 bar 변수를 참조할 수 없다.
let i = 10;
// ----- 함수 레벨 스코프
function foo(){
let i = 100;
// ----- 블록 레벨 스코프
for(let i=1; i<3; i++){
console.log(i); // 1, 2
}
// ----- 블록 레벨 스코프
console.log(i); // 100
}
// ----- 함수 레벨 스코프
foo();
console.log(i); // 10
이 경우도 마찬가지로 스코프가 블록 레벨 스코프 기준으로 나누어져 있기 때문에 , 다른 결과가 나오게 된다.
let의 Hoisting
var과 달리 let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.
console.log(foo); // error
let foo;
let 키워드로 선언한 변수를 변수 선언문 이전에 참조하면 , 참조 에러가 발생하게 된다.
그 이유는 var키워드는 런타임 이전에 선언단계와 초기화 단계가 한번에 발생한다. 즉 , 선언 단계에서 변수를 등록하고 초기화 단계에서 undefined로 초기화 한다. 그 이후 할당을 하는 방식이다.
하지만 let 키워드는 선언단계와 초기화 단계가 한번에 발생하지 않는다. 런타임 이전에 선언단계가 실행되지만 , 변수 초기화는 변수 선언문에 도착했을 때 이루어진다. 그래서 초기화 단계가 실행되기 전에 변수에 접근하려고 하면 참조 에러가 발생한다.
console.log(foo); // error 발생 , 선언 단계
let foo;
console.log(foo); // undefined , 초기화 단계
foo = 1;
console.log(foo); // 1 , 할당 단계
const
const 키워드는 상수를 설명하기 위해 사용하지만 , 반드시 상수만을 위해서 사용하지는 않는다. const의 특징은 대부분 let과 동일하지만 , 차이점이 존재한다.
1. const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.
const foo; // Error
{
console.log(foo); // Error
const foo = 1;
console.log(foo); // 1
}
console.log(foo); // Error
또한 블록 레벨 스코프를 가진다.
2. 재할당 금지
const foo = 1;
foo = 2; // Error
var, let 키워드로 선언한 변수는 재할당이 자유로우나 const 키워드로 선언한 변수는 재할당이 금지된다.
3. const 키워드로 선언한 변수에 객체를 할당한 경우 , 값을 변경할 수 있다.
const person = {
name : 'Lee;
};
person.name = 'Kim';
console.log(person); // {name: "Kim"}
즉 , const 키워드는 재할당을 금지할 뿐 "불변"을 의미하는 것은 아니다. 변수 선언에는 기본적으로 const를 사용하고 , 재할당이 필요한 경우 let을 사용하는 것이 좋다. ES6를 사용하는 경우 , var 키워드는 사용하지 않는다. 일단 const를 사용하고 , 재할당이 필요한 경우 let을 사용하는 것이 좋다.