var, let 그리고 const의 차이점 5가지

김용현·2024년 1월 25일
0

[cs]

목록 보기
5/5
post-thumbnail

Javascript는 es6 이전의 버전에서는 변수를 사용하기 위해 var라는 키워드를 사용했습니다. 하지만 var의 사용은 다음과 같은 문제를 일으키게 되었습니다.

  1. 변수를 중복해서 선언하여, 예기치 못한 문제를 야기할 수 있습니다.
  2. 변수를 선언하기 전 호출하여, 의도치 않은 undefined 값을 반환합니다.
  3. 함수 레벨 스코프로 인해, 함수가 아닌 블록에서 선언된 변수는 전부 전역변수가 됩니다.

Javascript는 앞선 문제들의 해결을 위해 es6 이후 let과 const라는 키워드를 도입하였고, var의 사용을 지양하도록 하였습니다.

그렇다면 var, let 그리고 const에는 정확하게 어떤 차이가 있을까요? 각각의 차이점은 어떻게 동작하고, 어떠한 문제들을 일으킬까요? 이 내용을 지금부터 5가지의 관점에서 함께 알아보고자 합니다.

1. 블록 레벨 스코프

함수 레벨 스코프

📌 함수 레벨 스코프(Function-level scope)
함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.

var 변수는 자바스크립트는 함수 레벨 스코프(Function-level scope)를 따릅니다.

var world = 1; // 전역 변수

console.log(world); // 1

{
	var world = 2; // 전역 변수, 코드 블록 내이지만 var는 함수 레벨 스코프를 따르기 때문
}

console.log(world); // 2, 코드 블록 내에서 재 선언된 world 변수가 값을 재할당하여 덮어썼기 때문

블록 레벨 스코프

📌 블록 레벨 스코프(Block-level scope)
모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.

대부분의 프로그래밍 언어는 블록 레벨 스코프를(Block-level scope)를 따르고 있으며 ES6 부터 자바스크립트 또한 블록 레벨 스코프의 변수를 선언하기 위해 let / const를 제공하고 있습니다.

let hello = 1; // 전역 변수

console.log(hello); // 1

{
	let hello = 2; // 지역 변수
	let world = 2; // 지역 변수,
}

console.log(hello); // 1
console.log(world); // ReferenceError: world is not defined

// world는 블록 레벨 스코프에 정의된 지역 변수이기 때문에 블록 밖에서는 해당 값을 참조할 수 없다.

2. 변수 중복 선언 금지

var 키워드의 경우 동일한 이름을 갖는 변수를 중복해서 선언할 수 있었습니다. 하지만 let은 값의 재할당은 가능하지만, 동일한 명의 변수를 재선언할 경우 문법에러(Syntax Error)가 발생하게 됩니다.

var의 중복 선언

var hello = "hello"
console.log(hello) // hello

var hello = "world"
console.log(hello) // world, 정상적으로 재할당

let의 중복 선언

let hello = "hello"
console.log(hello) // hello

let hello = "world" // SyntaxError: Identifier 'hello' has already been declared
console.log(hello)

3. 변수 hoisting

변수의 선언 3단계

Javascript에서 변수는 3가지의 단계를 걸쳐 생성되게 됩니다.

1️⃣ 선언 단계(Declaration phase)
변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.

2️⃣ 초기화 단계(Initialization phase)
변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서는 변수는 undefined로 초기화 된다.

3️⃣ 할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제 값을 할당한다.

var과 let/const는 각각의 선언 3단계가 실행되는 시점에서 차이가 있습니다. 이러한 차이는 TDZ를 발생시키고, 이는 var는 변수 hoisting이 가능하지만 var/const는 불가능하게 하는 차이점을 만들어 냅니다.

그래서 TDZ(Temporal Dead Zone)이 뭐야?

TDZ

특정 변수에 접근하기 위해서는 메모리에 변수를 위한 공간을 할당한 이후인 “초기화 단계” 를 거쳐야 합니다. 변수를 선언하는 것만으로는 해당 변수를 접근할 수 없고, 이 변수에 접근할 경우 참조 에러(Reference Error)가 발생합니다. 이렇게 변수의 스코프 시작 지점부터, 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 ‘일시적 사각지대(Temporal Dead Zone; TDZ)’라고 부릅니다.

var의 생명 주기

var의 생명 주기

코드가 실행되기 전, 브라우저 엔진이 평가를 통해 var로 선언된 hello는 이미 메모리 공간에 "undefined"의 값을 할당 받아 있다.

var의 경우 변수의 선언 단계와, 초기화 단계가 한번에 이루어지게 됩니다. 즉 스코프에 변수를 등록함과 동시에, 메모리에 해당 변수를 위한 공간을 할당 및 undefined로 값을 초기화 합니다.

따라서, 아직 var로 선언한 변수에 값을 할당하지 않은 상태에서 해당 값을 참조하더라도 undefined의 값을 반환하는 현상을 목격할 수 있습니다. 이러한 현상을 “변수 호이스팅”이라고 합니다.

// 스코프의 선두에서 선언 단계와 초기화 단계가 동시에 실행
// 이 시점에서 hello 변수는 초기화되어 undefined의 값을 할당 받는다
console.log(hello); // undefined

var hello;
hello = 1; // 이 시점에서 hello 변수에 1이 할당
console.log(hello); // 1

let과 const의 생명 주기

let의 생명 주기

let은 브라우저 엔진의 평가 단계에서 선언만 수행하고, 코드를 실행하여 변수를 선언하는 단계에 도달하면 변수에 메모리 공간을 부여하고 undefined의 값을 할당한다.

let의 경우 선언 단계와 초기화 단계가 분리되어 진행됩니다. 즉 코드를 해석한 시점에 스코프에 해당 변수를 등록하지만, 초기화는 해당 변수가 선언하는 단계에 도착해야만 수행하게 됩니다.

선언단계와 초기화 단계 사이에는 아직 let 변수를 위한 메모리가 할당 되지 않았기 때문에, 만약 해당 변수에 접근하게 될 경우 참조 에러가 발생하게 됩니다.

// 스코프의 선두에 선언 단계가 실행
// 이 시점에서 world 변수는 스코프에 등록될 뿐 값은 할당되지 않았다

console.log(world); // ReferenceError: world is not defined

let world // 변수를 선언할 때, 초기화가 수행된다.
console.log(world); // undefined

world = 1; // 할당문에서, world에 값이 할당된다.
console.log(world); // 1

단, const의 경우 선언과 동시에 할당이 이루어지지 않으면 문법 에러(SyntaxError)가 발생하게 됩니다.

const hello; // SyntaxError: Missing initializer in const declaration

4. 전역 객체

전역 객체(Global Object)는 모든 객체의 유일한 최상위 객체를 말하게 됩니다. 만약 전역 변수를 var 키워드로 선언한다면, 해당 값은 전역 객체의 프로퍼티가 됩니다.

전역객체에 저장되는 var

하지만, let 혹은 const로 선언된 변수는 전역 객체에서 선언되는 것이 아닌 “Declative Environment”의 렉시컬 환경에 따로 분리되어 선언되게 됩니다. 따라서 해당 값은 전역 객체의 프로퍼티로 활용할 수 없습니다.

전역 객체가 아닌 const와 let

5. 재할당

let과 var로 선언한 변수는 변수에 새로운 값을 할당할 수 있지만, const에는 재할당이 금지되어 있습니다.

const hello = "hello";
hello = "world"; // TypeError: Assignment to constant variable.

지금까지 var, let 그리고 const의 5가지 차이점을 알아보았습니다. 여러가지 복잡한 그리고 평소에 알지 못했던 차이점들을 학습할 수 있었는데, 특히 var의 변수 hoisting에 대해 더 잘 이해하고자 한다면 Javascript의 동작을 이해하는데 가장 중요한 내용 중 하나인 실행 컨택스트렉시컬 스코프에 대한 학습을 하시면 큰 도움이 될 것입니다.

참고 자료 정리

[PoiemaWeb] ex6-block-scope

profile
함께 일하고 싶은 개발자가 되기위해 노력 중입니다.

0개의 댓글