Front-end/깊게 파고들기

var, let, const 차이점 (호이스팅, 스코프)

아지송아지 2022. 3. 19. 20:49

안녕하세요

오늘은 var, let, const 에 대해 알아보겠습니다.

 

 

var


javascript에서 ES5까지 변수를 선언하는 방법은 var 뿐이었습니다.

이는 몇 가지 심각한 문제가 있습니다.

 

1. 함수 레벨 스코프

함수의 코드 블록만 스코프로 인정합니다. 전역 함수 외부에서 생성한 변수는 모두 전역 변수입니다.

 

2. var 키워드 생략 및 중복 선언 허용

암묵적으로 전역 변수를 생성할 가능성과 의도하지 않은 변수값의 변경이 일어날 가능성이 큽니다.

 

3. 변수 호이스팅

변수를 선언하기 이전에 참조할 수 있습니다.

 

이는 변수가 어디에서 어떻게 사용하는지 파악하기 힘들며 비순수 함수에 의하여 값이 변경될 때도 있습니다.

 

ES6에서는 이러한 문제점들을 보완하기 위하여 let과 const를 만들었습니다.

 

 

let


앞서 var를 설명할때 함수 레벨 스코프, 호이스팅이라는 단어를 사용했습니다.

let과 함께 두 단어를 설명드리겠습니다.

 

let은 중복 선언은 불가능하고 재할당은 가능합니다.

let foo = 123;
let foo = 456;  // Uncaught SyntaxError: Identifier 'bar' has already been declared

let bar = 123;
bar = 456; // 허용

 

블록 레벨 스코프 vs 함수 레벨 스코프

대부분의 언어는 블록 레벨 스코프를 따르지만 자바스크립트는 블록 레벨 스코프를 따릅니다.

 

스코프 (scope)

유효 범위라고 생각하시면 편합니다.

자바스크립트에서는 전역 스코프, 지역 스코프 두 가지로 구분할 수 있습니다.

 

함수 레벨 스코프 (Function-level scope)

함수 내에서 선언된 변수는 함수 내에서만 유효하며 외부에서는 참조할 수 없습니다.

즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수입니다.

 

블록 레벨 스코프 (Block-level scope)

모든 코드 블록(함수, if, for, while 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 외부에서는 참조할 수 없습니다.

코드 블록 내부에서 선언한 변수는 지역 변수입니다.

 

var foo = 123; // 전역 변수
console.log(foo); // 123

{
  var foo = 456; // 전역 변수
}

console.log(foo); // 456

var는 함수 레벨 스코프를 따른다고 하였습니다.

따라서 코드 블록 내에서 선언된 foo는 이미 전역에서 선언된 값에 재할당하여 덮어씁니다.

 

let foo = 123; // 전역 변수

{
  let foo = 456; // 지역 변수
  let bar = 456; // 지역 변수
}

console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined

let은 블록 레벨 스코프이기 때문에 코드 블럭 내부에서 선언한 변수는 외부에 영향을 주지 않습니다.

 

 

호이스팅

호이스팅이란 var 선언문이나 function 선언문 등 모든 선언문이 해당 스코프의 선두로 옮겨진 것처럼 동작하는 특성을 말합니다.

 

변수는 "선언 -> 초기화 -> 할당" 3단계에 걸쳐 생성됩니다.

선언 단계(Declaration phjase)

변수를 실행 컨텍스트의 변수 객체에 등록합니다. 스코프가 참조하는 대상이 됩니다.

 

초기화 단계 (Initialization phase)

변수 객체에 등록된 변수를 위한 공간을 메모리에 확보합니다. 변수는 undefined로 초기화 됩니다.

 

할당 단계 (Assignment phase)

undefined로 초기화된 변수에 실제 값을 할당합니다.

 

console.log(foo); // undefinded
var foo;
foo = 123;
console.log(foo); // 123

var는 선언과 초기화가 한번에 이루어집니다.

변수 호이스팅으로 인해 foo를 선언하기전에 콘솔로 foo에 접근하여도 스코프에는 변수가 존재하기 때문에 에러가 발생하지 않습니다. 하지만 초기화 단계에서 undefined로 초기화 시켰기 때문에 undefined가 콘솔에 찍힙니다.

 

 

// 호이스팅으로 선언 단계는 실행되지만 초기화는 아직이다.
console.log(foo); // ReferenceError: foo is not defined
let foo; // 초기화 단계 실행
foo = 123; // 할당
console.log(foo); // 123

반면 let은 선언과 초기화가 분리되어 진행됩니다.

스코프에 등록은 하지만 변수 선언문에 도달했을 때 초기화 단계가 이루어집니다.

그렇기 때문에 참조 에러가 발생합니다. 스코프 시작지점 ~ 초기화 시점(변수 선언) 사이의 구간은 변수를 참조할 수 없고 이 구간을 "일시적 사각지대(TDZ - Temporal Dead Zone)"이라고 부릅니다.

 

let foo = 1; // 전역 변수

{
  console.log(foo); // ReferenceError: foo is not defined
  let foo = 2; // 지역 변수
}

전역 변수 foo와 블록 안에서 지역 변수 foo가 선언되었습니다.

블록 안에서 호이스팅이 발생되기 때문에 콘솔에서는 참조 에러가 발생합니다. (TDZ)

 

 

전역 객체 var  vs  let

var foo = 123; // 전역변수
console.log(window.foo); // 123

let bar = 123;
console.log(window.bar); // undefined

var로 선언된 변수는 전역 객체의 프로퍼티가 됩니다.

let은 전역 객체의 프로퍼티가 아닌 개념적인 블록 내에 존재합니다.

 

이해를 돕기 위하여 아래 사진과 같이 개발자 도구로 살펴보았습니다.

사진에는 안 보이지만 var로 선언한 foo는 Global로 들어갔으며 let으로 선언한 bar는 Script라는 곳으로 따로 들어갔습니다.

 

 

 

 

 

const 


마지막으로 const입니다.

let과 대부분 동일하여 다른점만 살펴보겠습니다.

 

const는 상수입니다.

그렇기 때문에 재선언과 재할당이 불가능합니다.

또한 const는 반드시 선언과 동시에 할당이 이루어져야합니다.

 

객체

const의 값이 객체일 경우 한가지 유의하셔야합니다.

const user = { name : "song" };

user.name = "jaehyeok";
console.log(user); // { name : "jaehyeok" }

객체의 내용이 변경되더라도 할당된 주소값이 바뀌는게 아니기 때문에 오류가 발생하지 않습니다.

만약 주소값을 변경해야 한다면 let을 사용해야 합니다.

 

 

 

마무리


  • var는  사용하지 않는게 좋습니다.
  • 재할당 할 일이 없다면 const를 사용하시고 필요하다면 let을 사용하는게 좋습니다.

프로그래밍에서 가장 기본이 되는 변수에 대하여 알아보았습니다.

정리를 하며 쉽게 놓칠 수 있었던 호이스팅과 각 개념들 간의 관계에 익숙해지는 시간이었습니다.

 

 

 

참고

https://poiemaweb.com/es6-block-scope

https://www.youtube.com/watch?v=61iolhWgQt0 

 

'Front-end > 깊게 파고들기' 카테고리의 다른 글

Virtual DOM 이란?  (0) 2022.03.21
브라우저는 어떻게 동작하는가?  (0) 2022.03.20
클린 코드란 무엇일까  (0) 2022.03.02
React-Reudx 정리  (0) 2022.01.27
DOM Node, Element 정리  (0) 2022.01.03