js에서 변수의 선언은 var, const, let으로 한다. 이전에는 const, let 없이 var만 쓰였고, ES6부터 const와 let이 추가되었다.
var a // 변수 선언
a = '가나다' // 값 할당
var a = '가나다' // 변수 선언과 할당
변수 선언과 할당은 하나로 표현할 수 있지만, 각각 실행 시점이 다르다.
변수 선언은 호이스팅되어 런타임 이전에 실행되지만, 값의 할당은 소스코드가 순차적으로 실행되는 런타임에 실행된다.
const a = 1; // O
const b;
const b = 2; // error
이름에서 알 수 있다시피 처음 선언한 뒤 변경하지 않을 값을 할당할 때 쓴다.
const a = 1;
a = 2; // error
let a = 1;
a = 2; // O
var a = 1;
var a = 2;
console.log(a) // 결과값 2
var a = 3;
console.log(a) // 결과값 3
이렇게 해도 에러가 나지 않는다. 유연하게 변수 선언이 가능하지만 이것으로 인한 장점보다 중복 선언으로 인한 위험성이 더 크기 때문에 지금은 const와 let 사용을 권장하는 것 같다.
이외에 var와 let/const의 차이점을 더 알기 위해 먼저 호이스팅과 스코프에 대해 알아야 한다.
var과 const, let의 차이점을 설명하는 글을 볼 때마다 이 호이스팅이라는 것이 보여서 한 번 정리해보려 한다.
(hoisting은 '끌어 올리다'라는 뜻이다.)
var 변수 선언과 함수 선언문에서만 호이스팅이 일어난다.
var 변수/함수의 선언만 위로 끌어올려지며, 할당은 끌어 올려지지 않는다. let/const 변수 선언과 함수 표현식에서는 호이스팅이 발생하지 않는다.
console.log("가나다");
var a = '123';
let b = '321';
// 위의 코드 호이스팅 결과
var a; // 호이스팅 (선언)
console.log("가나다");
a = '123'; // 할당
let b = '321'; // 호이스팅 X
첫번째 코드를 js parser가 읽은 결과가 아래 코드이다. 호이스팅된 후에 코드를 표현한 것이다. 위의 설명처럼 var 변수만 호이스팅이 일어났고 모든 코드보다 위로 끌어올려진 것을 볼 수 있다. 따라서 아래와 같은 코드도 동작하게 된다.
console.log(a); // 결과값 : undefined
var a = '123';
var가 아닌 let/const였으면 에러가 나왔을 것이다. 결과값이 undefined가 나온 것은 위에서 볼 수 있다시피 호이스팅은 선언만 끌어 올리기 때문이다. 값은 할당되지 않았으니 undefined가 나온다.
일단 함수를 정의하는 네 가지 방식을 살펴보자.
// 1. 함수 선언문(함수 이름 생략 불가능)
function abc() {
console.log("123");
};
// 2. 함수 표현식(함수 이름 생략 가능)
var abc = function() {
console.log('123');
};
// 함수 이름 작성 시
var abc = function 123() {
console.log('123');
};
// 3. Function 생성자 함수
// 4. 화살표 함수
var abc = () => {
console.log('123');
};
이제 함수 선언문과 표현식에서의 호이스팅을 알아보자. 간단한 예시를 보면 다음과 같다.
f1();
f2();
// 함수 선언문
function f1() {
console.log('123');
}
// 함수 표현식
var f2 = function() {
console.log('321');
}
// 위의 코드 호이스팅 결과
var f2; // [호이스팅] 함수보다 먼저 변수값이 선언되는 것을 볼 수 있다.
function f1() { // [호이스팅] 함수 선언문
console.log('123');
}
f1();
f2(); // error 발생
f2 = function() {
console.log('321');
}
이렇게 함수 선언문만 호이스팅되는 것을 볼 수 있다. 함수 표현식의 경우 var로 선언했기 때문에 변수 부분만 호이스팅되고 함수 부분은 호이스팅되지 않았다. 따라서 f2는 선언만 된 상태기 때문에 실행시 에러가 발생한다. 또 둘 다 호이스팅 되었을 때, 변수가 함수보다 위에 오는 것을 알 수 있다.
!주의점
식별자(ex. 변수명, 함수명 등)의 유효 범위를 말하며, 선언된 위치에 따라 스코프가 달라진다.
전역에 선언된 변수는 전역 스코프를, 지역에 선언된 변수는 지역 스코프를 갖는다.
또 한가지, 자바스크립트에서 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만들며 이러한 특성을 블록 레벨 스코프라고 한다. 그리고 함수의 코드 블록만을 지역 스코프로 인정하는 것을 함수 레벨 스코프라고 한다.
const a = 1; // 전역 변수
if (true) {
const b = 2; // 지역 변수(블록 레벨 스코프)
};
function c() {
const d = 3; // 지역 변수(함수 레벨 스코프)
};
그런데 여기서도 var와 let/const의 차이가 나타난다. let/const는 함수 레벨 스코프는 물론 블록 레벨 스코프를 인정한다. 그러나 var는 함수 레벨 스코프만 인정한다.
let a = 1;
if (true) {
let a = 2;
};
console.log(a); // 결과값 : 1
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
var a = 1;
if (true) {
var a = 2;
};
console.log(a); // 결과값 : 2
let의 경우 if 안에서 선언된 것은 if 안에서만 쓰일 수 있다. 그러나 var의 경우 블록 레벨을 무시하므로 if 안의 값이 출력된 것이다. 왜 이런 것인가 살펴보면 이것도 호이스팅에 의한 것이라고 볼 수 있다. 호이스팅의 정의를 다시 보면
- 유효 범위 : 함수 블록 {} 안에서 유효
호이스팅의 범위가 함수 안까지라는 것이니 함수가 아니라면 if든 for든 무시하고 문서의 최상단으로 끌어올려지기 때문에 var는 함수 레벨 스코프만을 인정하는 것이다.