스코프를 공부하던 중,
var
,let
,const
의 스코프가 다르다는 것을 알게되었다.
사실, 필자가 프론트엔드 공부를 시작한 시점에는 이미let
,const
가 일반적으로 사용되는 시기였기 때문에var
를 단 한번도 사용한 적이 없다.
그래도 언젠가 취업했을 때 레거시 코드에서 등장할 수도 있고, 자바스크립트의 시초가 되는 것이기도하니 차이를 공부해놓고자 한다.
우선, 이들의 차이를 알아보기 전에 어떤 놈이 어떤 스코프를 가지는지 명시하고 넘어가려고한다.
더 자세한 설명은 필자의 스코프에 대한 게시글을 참고해주세요.
var
: 함수 레벨 스코프(Function-level scope)
let
, const
: 블록 레벨 스코프(Block-level scope)
함수 레벨 스코프
는 말그대로 함수 내에서 선언된 것은 함수 내에서만 유효하다는 것이다.
블록 레벨 스코프
는 {...}, 즉, 중괄호로 감싸진 범위 내에서만 유효하다는 것이다.
var
의 특징에 대해서 알아보자.
스코프
: 함수 레벨 스코프
재선언
: 가능
재할당
: 가능
아래 코드를 통해 살펴보자.
var a = 10;
console.log(a); // 10
var a = 20;
console.log(a); // 20
a = 30;
console.log(a); // 30
분명, 맨 위에서 변수 a
를 선언하고 할당했음에도 불구하고,
바로 다음에 다시 선언을 할 수가 있다.
또한, 재할당도 할 수 있다.
이번에는 스코프에 관련된 부분을 살펴보자.
for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log(i); // 3
함수 레벨 스코프
를 가지기 때문에 for
문 외부에서도 변수에 접근이 가능하다.
사실 이 부분은 약간 헷갈릴 수 있다.
아래 예시를 보자.
function varTest() {
var test = 1;
console.log(test);
}
varTest(); // 1
console.log(test); // ReferenceError: test is not defined
정말. 말그대로. 함수 내부에서 선언된 것은 함수 내부에서만 사용할 수 있다.
그 외에는 접근이 가능하다.(방금 for
문 예시처럼)
let
의 특징에 대해서 알아보자.
스코프
: 블록 레벨 스코프
재선언
: 불가능
재할당
: 가능
아래 코드를 통해 살펴보자.
let a = 10;
console.log(a); // 10
a = 20;
console.log(a); // 20
재할당은 가능하다.
그러나 재선언을 하게된다면?
let a = 10;
console.log(a);
a = 20;
console.log(a);
let a = 30; // SyntaxError: Identifier 'a' has already been declared
console.log(a);
에러가 발생한다.
같은 스코프 내에서는 변수를 재선언 할 수가 없다.
그러면 스코프가 달라질 때는 어떻게 될까?
function declaration() {
let a = 10;
console.log(a); // 10
a = 20;
console.log(a); // 20
}
function wantReDeclaration() {
let a = 30;
console.log(a); // 30
}
declaration();
wantReDeclaration();
같은 변수명을 가짐에도 코드가 작동한다.
이는 let
이 블록 레벨 스코프
를 가지기 때문이다.
중괄호({...}) 내에서 선언된 변수는 중괄호를 빠져나가면 유효하지 않기 때문이다.
따라서, var
변수에서 살펴봤던 for
문 예시는 아래와 같이 작동한다.
for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log(i); // ReferenceError: i is not defined
for
문의 중괄호를 빠져나오자, 유효하지않은 변수로 변한 것을 확인 할 수 있다.
const
의 특징에 대하여 알아보자.
스코프
: 블록 레벨 스코프
재선언
: 불가능
재할당
: 불가능
아래 코드를 통해 살펴보자.
const a = 10;
console.log(a); // 10
a = 20; // TypeError: Assignment to constant variable.
console.log(a);
재할당이 안되는 것을 확인 할 수 있다.
물론, 재선언도 안된다.
const a = 10;
console.log(a); // 10
const a = 20; // SyntaxError: Identifier 'a' has already been declared
console.log(a);
그렇다면 스코프를 다르게 했을 때는 어떨까?
function declaration() {
const a = 30;
console.log(a); // 30
}
function wantReDeclaration() {
const a = 10;
console.log(a); // 10
}
declaration();
wantReDeclaration();
스코프를 다르게 했더니 재선언이 가능하다.
이는 const
가 블록 레벨 스코프
를 가지기 때문에 가능한 것이다.
중괄호를 벗어나면 그 유효성을 잃는다.
호이스팅이란 변수,함수 선언이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말한다.
자세한 설명은 필자의 호이스팅에 대한 게시글을 참고해주세요.
아무튼, 이 얘기가 갑자기 왜 나오느냐?
var
와 let
,const
의 변수 선언에서의 동작 차이를 설명하기 위해서이다.
변수는 2단계를 거쳐서 생성된다.
1단계 : 선언(Declaration)
변수 이름을 실행 컨텍스트에 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
2단계 : 초기화(Initailization)
값을 저장하기 위한 메모리 공간을 확보하고, 암묵적으로 undefined를 할당해 초기화한다.
아래 코드를 살펴보자.
console.log(a); // undefined
var a;
요상한 코드다. 선언되기도 전에 변수를 사용했다.
그러나, 에러는 발생하지않는다.
호이스팅에 의해 var
의 선언이 최상단으로 끌어올려졌기 때문이다.(실제로는 그런 것처럼 보이는 것이다)
그런데...선언이 최상단으로 끌어올려진 것이지 초기화는 아니지않나?
왜 undefined
가 된거지?
이게 바로 var
와 let
,const
의 차이이다.
var
는 선언과 동시에 초기화까지 진행된다.
그러나 let
은 선언과 초기화가 별개로 작동한다.
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b;
초기화되지 않았기 때문에 undefined
가 출력되지 않고, 에러가 발생하는 것이다.
let
의 초기화는 let
이 존재하는 코드에 도착했을 때 이루어진다.
즉, 아래와 같이 코드를 작성해야, let
의 선언과 초기화가 동시에 된다는 것이다.
let b;
console.log(b); // undefined
호이스팅으로 인해 선언이 먼저 완료가 되며,
let
선언 코드 라인에 도착해서 초기화를 통해 undefined
를 할당한다.
const
는 할당까지 해주지 않으면 문법적으로 에러가 발생한다.
console.log(c);
const c; // SyntaxError: Missing initializer in const declaration
이 에러는 콘솔과 const
선언의 순서를 바꿔도 마찬가지이다. 문법적인 에러이기 때문이다.
const
를 사용할 때는 변수 선언 및 할당을 전부 해줘야 문법 에러가 나지 않는다.
하나몬님 블로그 - 스코프와 변수 선언
하나몬님 블로그 - 호이스팅
eunjinii님 블로그
LeoHeo님 깃허브