📝

5. var, let, const

 
var, let, const는 자바스크립트에서 변수 선언을 위해 사용한다. var는 ES6 이전의 유일한 변수 선언 방식이었지만 이후에는 var의 여러 단점을 보완하기 위해 letconst가 추가되었다. 지금부터 변수 선언의 3가지 방법에는 어떤 특징과 차이점이 있는지 크게 5가지로 알아보자.
var, let, const 차이점
  1. 중복선언
  1. 재할당
  1. 스코프(Scope)
  1. 호이스팅(Hoisting)
  1. 전역객체 프로퍼티

5-1. var

5-1-1. 중복선언

 
var는 중복선언이 가능한 변수이다. var로 선언한 변수는 재선언이 가능하며 마지막으로 할당된 값이 변수에 저장된다.
 
var a = 1; console.log(a); // 1 var a = 10; console.log(a); // 10 var a; console.log(a); // 10
 

5-1-2. 재할당

 
var는 값의 재할당이 가능한 변수이다. 반복해서 재할당이 가능하다는 점이 편리하지만 복잡한 코드를 쓸 때는 이전에 선언해둔 변수를 잊고 값을 재할당하게 되면 변수가 다시 덮어 씌어지는 경우가 있으니 유의하자. 이러한 단점을 보완하고자 let과 const가 나온 것이다.
 
var a = 5; a = 10; console.log(a); // 10
 

5-1-3. 스코프(Scope)

 
스코프란 말 그대로 직역하면 범위라는 뜻이자 프로그래밍 언어로는 변수에 접근할 수 있는 범위이다. 예를 들어 지역변수(함수 내에서 선언된 변수)와 전역변수(함수의 외부에서 선언된 변수)로 나뉘는데 지역변수에서 선언된 변수는 지역변수로 선언되어 참조할 수 있다. 이 경우 변수의 스코프는 함수 내부로 제한된다. var는 함수 레벨 스코프(Function-level scope)를 따른다.
 
function foobar() { var foo = 5; console.log(foo); } foobar(); // 5 console.log(foo); // Uncaught ReferenceError: foo is not defined
 
foobar함수 내부에서 선언된 foo변수는 함수 내부에서만 선언이 가능하며, 외부에서 선언 시 에러가 발생한다.
 
if(true) { var a = 5; console.log(a); // 5 } console.log(a); // 5
 
함수를 제외한 영역에서 var로 선언한 변수는 전역변수로 간주된다.

5-1-4. 호이스팅(Hoisting)

 
호이스팅(Hoisting)이란, 자바스크립트 엔진이 소스코드 평가 과정에서 변수 선언을 포함한 모든 선언문을 먼저 찾아내어 실행하고 이후 모든 선언문을 제외한 소스코드를 순차적으로 실행하는데, 이때 선언문이 최상단으로 끌어올려져 동작하는 것을 말한다. 즉, 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.
var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다.
 
💡
다음 단계로 나누어 볼 수 있다.
  • 선언 단계 : 변수를 실행 컨텍스트에 등록(스코프가 참조할 수 있게 한다.)
  • 초기화 단계 : 선언 단계의 변수를 위한 메모리 공간 확보(undefined를 할당해 초기화)
  • 할당 단계 : 초기화 단계의 메모리에 값을 할당
console.log(a); // undefined var a = 5; console.log(a); // 5
 
다음은 undefined와 ‘hello world’가 출력이 된다. 처음에 hello 변수가 호이스팅 되어 undefined가 찍히고 그다음 hello에 ‘hello world’가 할당되어 그다음 콘솔에서는 ‘hello world’가 찍히게 된다. 여기서 3번째 줄에 있는 var hello가 선언부라고 하여 4번째 줄을 undefined로 만들지 않는다는 점을 유의하자.
 
var hello; console.log(hello); // undefined hello = 'hello world'; console.log(hello); // hello world
 

5-1-5. 전역객체 프로퍼티

전역 객체를 사용하면 어디서나 사용할 수 있는 변수나 함수를 만들 수 있다. var로 선언한 변수는 전역객체(브라우저 환경에서 window)의 프로퍼티(속성)로 할당된다.
 
var a = 5; console.log(window.a); // 5 console.log(a); // 5

5-2. let

5-2-1. 중복선언

let은 같은 변수를 중복 선언할 수 없다.
 
let hello = 1234; let hello = 5678; // SyntaxError: Identifier 'hello' has already been declared
 
위의 예시처럼 let을 사용하여 이름이 같은 변수를 중복 선언하게 되면 문법에러(SyntaxError)가 발생한다.
 

5-2-2. 재할당

let은 중복 선언이 불가능하지만 값의 재할당은 가능하다.
 
let hello = 100; // 최초 선언 hello = 200; // 값의 재할당 console.log(hello); // 200
 

5-2-3. 스코프(Scope)

let은 블록 레벨 스코프(block-level scope)를 따른다. 이는 모든 코드 블록(함수, if문, for문, while문 등)에서 선언된 변수를 지역변수로 인정한다.
 
let hello = 1; { let hello = 10; let world = 20; } console.log(hello); // 1 console.log(world); // ReferenceError: world is not defined
if(true) { let hello = 123; console.log(hello); // 123 } console.log(hello); // ReferenceError: hello is not defined
 

5-2-4. 호이스팅(Hoisting)

let으로 선언한 변수는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 아래의 설명과 예시를 살펴보면 호이스팅이 발생한다는 것을 알 수 있다.
 
console.log(hello); // ReferenceError: hello is not defined let hello; // 초기화 단계 console.log(hello); hello = 99; // 할당 단계 console.log(hello);
 
let으로 선언한 변수는 '선언 단계'와 '초기화 단계'가 분리되어 진행된다.
  • 선언 단계 - 일시적 사각지대 - 초기화 단계 - 할당 단계
💡
일시적 사각지대(Temporal Dead Zone) : 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간
 
let hello = 10; // 전역 변수 { console.log(hello); // ReferenceError: hello is not defined let hello = 2; // 지역 변수 }
 

5-2-5. 전역객체 프로퍼티

let으로 선언한 변수는 전역 객체의 property가 아니다.
 
let hello = 123; console.log(hello); // 123 console.log(window.hello); // undefined

5-3. const

5-3-1. 중복선언

const는 변수를 중복해서 선언할 수 없다.
 
// 첫번째 선언 const kakao = 1; // 중복선언 const kakao = 2; // Uncaught SyntaxError: Identifier 'kakao' has already been declared
 
위와 같이 두 번째 줄에 이름이 같은 변수를 선언하는 순간 Syntax Error가 발생한다.
 

5-3-2. 재할당

var와 let은 값을 재할당할 수 있는 변수인 반면에 const는 값을 한 번 설정하고 나면, 다른 값을 다시 할당할 수 없다.
 
// 선언단계 const kakao = 1; // 할당단계 kakao = 2; // Uncaught TypeError: Assignment to constant variable.
 
첫 번째 선언한 kakao에 ‘2’를 재할당하려고 하면 Type Error가 발생한다.
 
※ 주의 : const는 선언과 할당을 동시에 해주어야 한다. 만약 값을 할당하지 않고 상수만 선언해버리면 Syntax Error가 발생한다.
 
const kakao; // Uncaught SyntaxError: Missing initializer in const declaration
 
const로 객체 프로퍼티 변경하기
  • 할당된 객체의 프로퍼티는 변경할 수 있다.
  • 재할당은 안되지만, 할당된 그 객체의 내용물은 변경할 수 있다.
 
※ 주의 : 설정(=할당)된 주소 값은 변경되지 않는다.
 
// 객체 프로퍼티 변경가능 const company = { name: 'kakao' }; company.name = 'naver'; console.log(company); // {name: 'naver'}
// 객체 프로퍼티 변경가능 const value = [10, 20, 30]; value.push(100); console.log(value); // [10, 20, 30, 100]
 

5-3-3. 스코프(Scope)

const는 let과 마찬가지로, 블록 레벨 스코프(block-level scope)를 따른다. 블록 내에서 변수가 선언되었기 때문에 지역변수의 개념으로 인정된다. 해당 변수는 block 내에서만 유효하며, 외부에서 접근(=참조)할 수 없는 상태가 된다.
 
// block level scope { const kakao = 1; console.log(kakao); //1 } console.log(kakao); // ReferenceError: kakao is not defined
 
block을 벗어나게 되어서 에러를 일으킨다.
 
💡
블록(block)이란?
for문과 if문에서 중괄호 { }로 되어있는 부분을 지칭한다.
 

5-3-4. 호이스팅(Hoisting)

let 부분에서도 언급했듯이, const도 TDZ로 인한 제약을 받는다.
 
💡
TDZ(Temporal Dead Zone)이란?
초기화되지 않은 변수가 있는 곳을 TDZ(Temporal Dead Zone)라고 부른다. 짧게 이해하기 힘든 개념이라 해당 개념에 대한 좋은 포스팅이 있는 링크를 공유한다.
 
// const console.log(kakao); const kakao = 'Hi'; // ReferenceError: kakao is not defined
 
변수가 초기화되기 전에 접근하려고 해서 Reference Error가 발생된다.
  1. kakao가 호이스팅으로 기억된다.
  1. kakao가 초기화 되는 것은 2번째 줄이다.
  1. 실제 선언되고 할당되는 라인이 아니라서 kakao를 쓸 수 없다.
 
// var console.log(kakao); // undefined var kakao = 'Hi';
 
var는 변수가 초기화되기 전에 접근하면, undefined를 반환한다.
 
const apple = 'outer scope'; (function() { console.log(apple); const apple = 'inner scope'; }()); // ReferenceError: Cannot access 'apple'
 
letconst는 호이스팅이 되지 않는다는 오해를 종종 받는다. 그러나 const 선언도 호이스팅을 수행한다. 호이스팅이 발생했으나 값을 참조할 수 없기 때문에 호이스팅이 이뤄지지 않는 것처럼 보이는 특징이 있다. 하지만 호이스팅이 발생한다는 증거로 위의 코드를 참고해볼 수 있다. 코드에서는 미처 초기화하기 전에 접근하려 했기 때문에 에러를 띄운다. 함수 바깥의 전역변수 apple은 출력해야함에도 불구하고 호이스팅 되었기 때문에 에러가 발생된다.

5-3-5. 전역객체 프로퍼티

let과 마찬가지로 const로 선언한 변수는 전역 객체의 property가 아니다.
 
const kakao = 1; console.log(window.kakao); // undefined console.log(kakao); // 1
 
콘솔에 window를 찍어보면, const가 window의 property가 아니란 사실을 알 수 있다.

var, let, const 정리

var, let, const 의 특징을 간단히 정리하면 아래의 표와 같다.
중복선언
재할당
스코프 (Scope)
호이스팅 (Hoisting)
전역객체 프로퍼티
var
가능
가능
함수레벨 스코프 (Function-level)
호이스팅 시 undefined로 변수 초기화 (호이스팅 수행)
할당
let
불가능
가능
블록레벨 스코프 (block-level)
선언 단계-TDZ-초기화 단계-할당 단계로 분리되어 진행 (호이스팅 수행)
undefined
const
불가능
불가능
블록레벨 스코프 (block-level)
초기화 이전 접근 시 ReferenceError발생 (호이스팅 수행)
undefined
 

Reference


  1. https://curryyou.tistory.com/192
  1. https://noogoonaa.tistory.com/78
  1. https://medium.com/korbit-engineering/let과-const는-호이스팅-될까-72fcf2fac365