업데이트:

[01] var 키워드로 선언한 변수의 문제점

  1. 변수 중복 선언 허용

의도하지 않은 변수값의 변경이 일어날 수 있다.

  1. 함수 레벨 스코프

전역 함수 외부에서 생성한 변수는 모두 전역 변수이다. 함수의 코드 블록만 스코프로 인정하기 때문에 for문에서 선언한 변수를 for 문 외부에서 참조할 수 있다.

이로 인해 의도치 않게 전역 변수가 중복 선언되는 경우가 발생한다.

  1. 변수 호이스팅
console.log(foo); //undefined
foo = 123; 

위 코드처럼 변수 선언문 이전에 변수를 참조한는 것이 가능했다. 에러가 발생하진 않지만 프로그램의 가독성을 떨어트리고 오류를 발생시킬 여지를 남긴다.

  1. var 키워드 생략 허용

var 키워드 없이 선언이 가능해서 암묵적 전역 변수를 양산할 가능성이 크다.

[02] let 키워드

var 키워드의 단점을 보완하기 위해 let과 const를 도입했다. var 키워드와의 차이점을 살펴보자

변수 중복 선언 금지

var foo = 123; 
var foo = 456; // 중복 선언 가능

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

let 키워드를 사용하면 같은 변수 중복 선언시 문법 에러(syntaxError)가 발생한다.

블록 레벨 스코프

함수 레벨 스코프를 따르는 var 키워드와 달리 let 키워드는 블록 레벨 스코프를 따른다.

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

var foo1 = 123; // 전역 변수

console.log(foo1); // 123

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

console.log(foo1); // 456

let foo2 = 123; // 전역 변수

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

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

foo2 변수와 bar변수는 블록문 내의 지역변수이다. 전역에서 선언된 foo2 변수와 코드 블록 내에서 선언된 foo2 변수는 별개이다.

만약 함수 내에 코드 블록이 생겨도 함수 레벨 스코프에 중첩되고 그 안에서 블록 레벨 스코프를 만든다.

변수 호이스팅

console.log(foo); // undefined
var foo;

console.log(bar); // Error: Uncaught ReferenceError: bar is not defined
let bar;

let 키워드는 선언문 이전에 참조하면 참조 에러가 발생한다.

let 키워드로 선언된 변수는 일시적 사각지대(TDZ)에 빠지기 때문이다.

🔍 일시적 사각지대란?

일시적 사각지대를 설명하려면 먼저 변수 생성 과정을 자세히 알아야 한다.


선언 단계(Declaration phase)

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

초기화 단계(Initialization phase)

변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.

할당 단계(Assignment phase)

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


var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다.

Untitled

let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.

이때 스코프의 시작 지점(변수 선언 지점)부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 일시적 사각지대(TDZ)라고 한다.

Untitled

그렇다면 let 키워드는 변수 호이스팅이 발생하지 않는가??

let foo = 1;
{
	console.log(foo); //ReferenceError: Cannot access 'foo' before initialization
	let foo = 2;
}

그렇지 않다. 호이스팅은 발생하는데 초기화만 일어나지 않아서 참조 에러가 발생하고 뒤에 초기화 전이라는 메세지가 적혀있다.

전역 객체와 let

전역 객체는 모든 객체의 유일한 최상위 객체이다.

let foo = 123; // 전역변수

console.log(window.foo); // undefined
console.log(foo); // 123

let 키워드를 사용하면 window.foo와 같이 접근할 수 없다. let 전역 변수는 보이지 않는 개념적인 블록 내에 존재하게 된다.

[03] const 키워드

const는 상수를 위해 사용한다. const는 let 과 거의 비슷한데 차이점을 중심으로 알아보자

const 특징

  • let은 재할당이 자유롭지만 const는 금지된다.
const FOO = 123;
FOO = 456; // TypeError: Assignment to constant variable.
  • const는 반드시 선언과 동시에 할당이 이루어져야 한다.
const FOO; // SyntaxError: Missing initializer in const declaration
  • const는 let과 같이 블록 레벨 스코프를 갖는다.
{
  const FOO = 10;
  console.log(FOO); //10
}
console.log(FOO); // ReferenceError: FOO is not defined
  • 상수는 가독성과 유지보수의 편의를 위해 적극적으로 사용해야 한다.
// 10의 의미를 알기 어렵기 때문에 가독성이 좋지 않다.
if (rows > 10) {
}

// 값의 의미를 명확히 기술하여 가독성이 향상되었다.
const MAXROWS = 10; // 여러 단어로 이루어진 경우에는 _로 구분하여 표현
if (rows > MAXROWS) {
}

const 키워드와 객체

const user = { name: 'Lee' };

// const 변수는 재할당이 금지된다.
// user = {}; // TypeError: Assignment to constant variable.

// 객체의 내용은 변경할 수 있다.
user.name = 'Kim';

console.log(user); // { name: 'Kim' }

const 변수의 재할당은 금지되지만 불변하지는 않는다. 동적으로 프로퍼티를 생성, 삭제, 값의 변경을 할 수 있다.

객체의 내용은 변경되지만 참조하고 있는 주소 값은 변하지 않기 때문에 재할당할 것이 아니라면 객체를 선언할 때는 const를 사용하는 편이 좋다.

[04] var vs. let vs. const

이 세가지 키워드의 차이점을 아는 것은 중요한것 같아서 책의 내용을 모두 가져왔다.

면접에서도 자주 질문한다고 하니 외워두자 !


변수 선언에는 기본적으로 const를 사용하고 let은 재할당이 필요한 경우에 한정해 사용하는 것이 좋다. 원시 값의 경우, 가급적 상수를 사용하는 편이 좋다. 그리고 객체를 재할당하는 경우는 생각보다 흔하지 않다. const 키워드를 사용하면 의도치 않은 재할당을 방지해 주기 때문에 보다 안전하다.

var와 let, 그리고 const는 다음처럼 사용하는 것을 추천한다.

  • ES6를 사용한다면 var 키워드는 사용하지 않는다.
  • 재할당이 필요한 경우에 한정해 let 키워드를 사용한다. 이때 변수의 스코프는 최대한 좁게 만든다.
  • 변경이 발생하지 않는(재할당이 필요 없는 상수) 원시 값과 객체에는 const 키워드를 사용한다. const 키워드는 재할당을 금지하므로 var, let 보다 안전하다.

변수를 선언하는 시점에는 재할당이 필요할지 잘 모르는 경우가 많다. 그리고 객체는 의외로 재할당을 하는 경우가 드물다. 따라서 변수를 선언할 때에는 일단 const 키워드를 사용하도록 하자. 반드시 재할당이 필요하다면(반드시 재할당이 필요한지 한번 생각해 볼 일이다.) 그때 const를 let 키워드로 변경해도 결코 늦지 않는다.

댓글남기기