가치투자자

[JS/기술면접] var, let, const와 TDZ 본문

Programming/JavaScript

[JS/기술면접] var, let, const와 TDZ

미주민 2023. 4. 2. 07:12
728x90
반응형

 var, let, const 

변수를 선언할 때 사용하는 키워드에는 var와 let, const가 있다.

2015년 ES6가 등장하기 이전에는 var 키워드로 변수를 선언해왔는데, let과 const라는 키워드가 새로 등장했다.

이러한 키워드들로 선언된 변수들이 어떻게 다른지 살펴보고자 한다.

 

* 기술면접 질문은 맨 아래에 적혀 있습니다

 

728x90

1-1. 변수 생성 과정

변수는 총 3단계를 거쳐 생성된다.

 

  1. 선언 단계 (declaration phase)
    : 변수를 (실행 컨텍스트의) 변수 객체에 등록한다. 즉, 변수가 등록(선언)된 것이다

  2. 초기화 단계 (initialization phase)
    : 변수에 값을 넣기 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.

  3. 할당 단계 (assignment phase)
    : undefined로 초기화된 변수에 실제 값을 넣어준다.

 

1-2. 일시적 사각지대 (TDZ)

var 키워드의 특징과 let과 const 키워드의 탄생을 이해하기 위해선 먼저 "일시적 사각지대(TDZ, Temporal Dead Zone)"에 대해 알고 갈 필요가 있다.

 

var 변수가 생성되는 과정에서 선언단계와 초기화 단계가 동시에 일어난다.

즉, 선언과 동시에 변수 값을 넣을 공간을 확보하고, 거기에 undefined라는 값을 넣어두는 것이다.

아래의 코드를 살펴보면, 아직 변수 foo의 선언문이 등장하지 않았지만, 변수 foo에 접근해 foo를 알고 있다는 듯이 undefined라는 값을 출력할 수 있다.

 

console.log(foo);  // 선언과 초기화 단계, undefined;

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

foo = 1;  // 할당 단계
console.log(foo);  // 1

 

 

반면, let과 const 키워드는 선언단계와 초기화 단계 사이에 일시적 사각지대(TDZ)라는 장벽을 만들어 변수 foo의 선언문이 등장하지 않은 시점에선 그 변수에 접근조차 하지 못하게 막아두었다.

즉, foo라는 변수가 등장도 안했는데, foo를 알고 있다는건 말이 안 되기 때문이다.

따라서 foo의 선언문이 등장하기 전에는 foo를 모른다고 참조에러(ReferenceError)를 띄워준다.

 

console.log(foo);  // ReferenceError: foo is not defined
                   // ReferenceError: Cannot access 'foo' before initialization

let foo;   // 변수 선언문 === 초기화 단계
console.log(foo);  // undefined

foo = 1;  // 할당 단계
console.log(foo);  // 1

 

TDZ에 대한 이해를 바탕으로 var 키워드의 특징과 문제점, 그리고 이를 보완하기 위해 등장한 let과 const 키워드의 특징을 살펴보자

 


2-1. var 키워드

 var 키워드 는 자바스크립트 ES5까지 변수를 선언할 때 사용되는 키워드였다.

이러한 var 키워드는 다음과 같은 특징을 지닌다.

 

1) 변수 중복 선언이 가능하다

var age = 20;
var age = 30;

console.log(age);  // 30

변수를 중복 선언하여 값을 바꿀 수 있다.

그러나 중복 선언으로 값을 쉽게 바꿀 수 있다면, 코드 길이가 매우 길 때 이전에 선언해둔 (존재를 잊어버린) 변수 값을 쉽게 바꿔 문제가 일어날 수 있다.

 


2) 변수 호이스팅

console.log(a);  // undefined
var a = 1;

console.log(a);  // 1

콘솔을 찍기 이전에 변수 a가 선언되어 있지 않지만, 호이스팅으로 인해 맨 상단으로 끌어올려져 변수 a의 값에 접근할 수 있다.

var 변수는 선언단계와 초기화 단계가 동시에 일어나기에 undefined라는 값을 불러올 수 있다.

 

즉, 원래라면 아직 변수 a의 존재를 모른다면 모른다고 참조 에러를 띄워줘야 하지만, var 변수를 맨 상단으로 끌어와 아무런 에러도 발생시키지 않고 undefined라는 값을 주는 것이다.이것이 어떤 문제가 되는지는 아래에서 좀 더 자세히 살펴보겠다.

 

호이스팅에 대해 더 자세한 내용은 아래 글을 참고하길 바란다

https://valueengine.tistory.com/15

 

[JavaScript] 호이스팅(hoisting)이란?

호이스팅 (Hoisting) 공식문서를 살펴보면, 호이스팅(hoisting)은 '인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것'을 의미한다고 나와있다. 역시 공식 문서의 용어들은 초보

valueengine.tistory.com

 


3) 함수 레벨 스코프

 스코프(scope) 에 대해 간단하게 말해 식별자(변수, 함수, 클래스의 이름)가 유효한 범위를 말한다.

서울에 A라는 사람이 살고 있을 때, 서울이라는 범위 내에선 A의 존재를 찾을 수 있고, 서울 밖에서는 A를 찾을 수 없다. 이때 서울이 스코프인 것이다. 만약 서울 외 지역에서 A를 찾게되면, 참조에러(ReferenceError)가 발생할 것이다.

 

스코프에는 2가지 종류가 있다.

 

  1. 전역 스코프 (global scope)

    - 코드의 가장 바깥 영역

    - 전역 변수 : 어디서든 참조 가능

  2. 지역 스코프 (local scope)

    - 블록이나 함수 몸체 내부

    - 지역변수 : 자신의 지역(스코프)에서만 참조 가능

전역 스코프는 대한민국 전체(서울 외)라고 하고 B라는 사람이 살고있다.

지역 스코프를 서울이라 하고 A라는 사람이 살고 있다.

서울은 대한민국이기에 서울에서 대한민국 사람 B를 부를 수 있다. 그러나 서울 외 대한민국에서 서울사람 A를 찾을 순 없다.

 

이러한 지역 스코프에는 함수 스코프(함수 내부)블록 스코프(if, for, while, try/catch 등)가 존재한다.

 

 

 

스코프에 대해 더 자세한 내용은 아래 글을 참고하길 바란다.

https://valueengine.tistory.com/20

 

[JavaScript] 스코프(scope)란?

스코프 (scope) 변수와 더불어 모든 프로그래밍 언어에서 가장 기본적인 개념 중 하나가 스코프(scope)이다. 그만큼 중요하기에 완벽하게 자신의 것으로 만들 필요가 있다. 1. 스코프란? 공식 문서를

valueengine.tistory.com

 

 

 var 키워드 는 오직 함수만 지역 스코프로 인정해주며, 이러한 특징을  함수 레벨 스코프 라고 한다.

아래의 예시를 살펴보자.

 

CASE 1

var 키워드는 오직 함수만 지역 스코프로 인정한다. 따라서 if문이 var x 변수를 감싸고 있어도 저 if문 블록은 없는 것과 마찬가지다.

따라서 var x = 1에 var x = 10로 중복 선언해줘서 값을 바꿔버린다.

var x = 1;

if (true) {
  var x = 10;
}
console.log(x); // 10

------------------------

// if문의 블록 스코프를 인정하지 않기에 아래와 같은 상황인 것이다.
var x = 1;
var x = 10;
console.log(x); // 10

 

CASE 2

원래라면 for문이 i를 감싸고 있어 전역 변수 var i의 값 10에 영향을 안 줘야 하지만, var 키워드는 함수 레벨 스코프이기에 for문 블록을 무시해버린다.

따라서 원래라면 전역에 있는 i에는 영향을 주지 않아야 하지만, for문 블록을 무시해 최종적으로 5가 출력된다.

var i = 10;

for (var i = 0; i < 5; i++) {
  console.log(i);  // 0 1 2 3 4
}

console.log(i); // 5

 


4) var 키워드 생략 가능

마지막으로, JavaScript 언어가 얼마나 자유분방하게 탄생한 언어인지 알 수 있는 특징이다.

바로 var 키워드를 생략해도 변수 선언이 가능하다는 것이다.

var a = 1;

if (a = 1) {
    b = 2;
}
console.log(a);  // 1
console.log(b);  // 2

 


2-2. var 키워드의 문제점

위와 같은 var 키워드의 특징들은 수많은 문제점을 일으킨다.

 

1) 변수 중복 선언 허용

변수를 중복 선언을 허용함으로써 멋대로 변수값을 바꿀 위험성이 있다

 

2) 변수 호이스팅

코드의 순서성 아직 선언되지 않은 변수를 호출할 경우 참조 에러를 보여줘야하지만, var 변수는 맨 상단으로 호이스팅 되고, 선언과 초기화가 동시에 이루어지므로 undefined라는 값을 출력해준다. 원래라면 위에서부터 아래로 순차적으로 코드가 실행되는게 이해하기 쉽지만, 이렇게 호이스팅 될 때 쉽게 접근이 가능하여 undefined라는 값을 출력해주면 코드의 순서를 이해하는데 방해가 된다.

즉, TDZ가 없어 접근이 쉬운 var 변수가 호이스팅되기에 var 변수 선언 전에 접근이 가능하고, 이는 코드의 흐름과 코드 실행 순서를 다르게 만들어 코드의 가독성을 떨어트릴 뿐만 아니라, 오류 발생 가능성도 존재하게 만든다.

 

3) 함수 레벨 스코프

오직 함수 스코프만 지역 스코프로 인정해 잘못된 값을 출력할 우려가 있다

 

 


3-1. let과 const

이처럼 var 키워드로 선언된 변수는 많은 문제점을 가지고 있다. 이러한 문제점들을 보완하기 위해 ES6부터 let과 const 키워드가 등장하게 되었다.

 

 let 키워드 는 ES6부터 등장한 변수 키워드이다.

let 키워드로 선언한 변수는 재할당이 가능하다. 즉, 값을 바꿀 수 있다는 것이다.

그러니  const 키워드 는 재할당이 불가능한 키워드다.

그렇기에 값을 바꾸려하면 값을 재할당할 수 없는 타입이라고 타입에러(TypeError)를 띄운다.

let a = 1;
a = 2;
console.log(a);  // 2

const b = 3;
b = 4;   // TypeError: Assignment to constant variable.
console.log(b);

 


3-2. let과 const의 특징

1) 변수 중복 선언이 불가능하다

let age = 20;
let age = 30;  // SyntaxError: Identifier 'age' has already been declared

만약 let 변수 키워드를 사용해 중복 선언을 하려하면, 이미 선언되어 있다고 문법에러(SyntaxError)를 띄운다.

 

const age = 20;
const age = 30;  // SyntaxError: Identifier 'age' has already been declared

const 키워드 역시 이미 선언되어 있다고 에러 메시지를 띄워준다.

즉, var 키워드의 중복 선언 문제점을 해결하기 위해 보완된 것이다.

 


2) 변수 호이스팅

let과 const 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.

아래의 코드를 살펴보면, 변수 a가 호이스팅 되지 않아 a를 찾을 수 없는건 아닐까 하고 생각할 수 있다.

console.log(a);  // ReferenceError: a is not defined
                 // ReferenceError: Cannot access 'a' before initialization
let a;

 

하지만 let과 const 키워드로 선언된 변수 역시 호이스팅 된다.

그 증거는 "초기화 전에 a에 접근할 수 없다"라는 참조 에러 메시지다.

만약 a가 선언 자체가 되어 있지 않다면, 첫 번째 에러 메시지만 보여줘야 한다. 그러나 초기화 전에 접근할 수 없다는 메시지로 보아 변수 a가 호이스팅되어 접근은 가능하지만 아직 초기화가 되지 않았다는 것이다.

 


3) 블록 레벨 스코프

함수 레벨 스코프를 사용하는 var 키워드와 달리 let과 const는 블록 레벨 스코프를 특징으로 한다.

그렇기에 전역 변수 i와 블록 스코프인 for문 안에 있는 지역 변수는 서로 다른 것이다. 따라서 for문 내에서 i 값이 계속 바뀌더라도 전역 변수 i의 값은 바뀌지 않기에 10을 출력한다.

이로 인해 블록 스코프 내에 있는 지역 변수였지만 전역변수로 취급하는 var 키워드의 한계를 개선할 수 있다.

 

let i = 10;

for (let i = 0; i < 5; i++) {
  console.log(i);  // 0 1 2 3 4
}

console.log(i); // 10

 


3-2. const의 특징

let 키워드와 달리 const 키워드만 가지는 특징을 살펴보겠다.

 

1) 선언과 초기화

let은 아래처럼 실제 값을 할당해주지 않고 선언만 했더라도 undefined가 할당되어 초기화가 된다.

let a;
console.log(a);  // undefined

a = 1;
console.log(a);  // 1

 

그러나 const는 반드시 선언과 동시에 실제 값을 할당해줘야 한다.

그렇지 않을 경우 문법 에러(SyntaxError)가 발생한다.

const a = 1;     // 선언과 동시에 할당
console.log(a);  // 1

const b;  // SyntaxError: Missing initializer in const declaration
b = 2;
console.log(b);

 


2) 재할당 금지

var 변수와 let 변수는 재할당이 자유롭지만, const 변수는 한번 선언하면 값을 바꿀 수 없다(재할당할 수 없다).

만약 재할당을 하려 한다면 "값을 재할당할 수 없는 타입"이라고 타입에러(TypeError)를 띄워준다.

const a = 1;
a = 2;  // TypeError: Assignment to constant variable

 

그렇지만 const 변수에 원시값(Primitive Type)이 아니라 참조값(Reference Type)이 할당되어 있으면, 값을 바꿀 수 있는 방법이 생긴다.

즉, 객체 내부의 값을 변경해주는 방식이다.

이것이 가능한 이유는 const 변수에는 상수 배열(변하지 않는 배열)이 할당된 것이 아니라, 상수 reference(변하지 않는 주소), 즉, 배열의 주소(reference)가 할당된 것이기 때문이다.

따라서 주소가 바뀌지 않는 이상 그 주소에 누가 살고 있는지는 문제가 되지 않는다.

const age = [15, 30, 45];

age.push(50);      // 값 추가
console.log(age);  // [ 15, 30, 45, 50 ]

age.shift();       // 값 제거
console.log(age);  // [ 30, 45, 50 ]

age[0] = 20;       // 값 재할당
console.log(age);  // [ 20, 45, 50 ]

 

그러나 주소 자체를 바꾸려 한다면, 에러 메시지가 뜬다.

따라서 객체를 const 키워드로 선언해주고, 그 내부의 값을 변경해주는 방식으로 코드를 짜면 좋다.

const age = [15, 30, 45];

age = [20, 30, 40];  // TypeError: Assignment to constant variable.

 


3) 상수

const 키워드는 불변의 값, 즉 상수를 선언할 때 주로 사용된다.

만일 값을 변경하고 싶다면, 재할당해주는 것이 아니라 const 변수 선언문에서 값을 바꿔주면 된다.

그리고 상수를 선언할 땐, 그것이 의미하는 바가 무엇인지 알 수 있도록 변수명을 설정해주는 것이 좋다.

 

const STAR = '*';

for (let i=1; i < 4; i++) {
  console.log(STAR.repeat(i));
}

// *
// **
// ***

 

반응형

 


🌟 자바스크립트 기술면접 질문

1. var 키워드의 문제점은 무엇이 있나요? 🔥

 

  1. 변수의 중복 선언을 허용해줘 변수 값을 멋대로 바꿀 위험이 있다
  2. var 변수는 TDZ가 없기에 변수 선언 전에 호출하여도 var 변수가 호이스팅되어 undefined라는 값을 출력할 수 있다. 이처럼 호이스팅으로 인해 코드를 읽는 순서와 실행 순서가 다른데도 값을 출력하게 되면 코드의 가독성을 떨어트리고, 오류 가능성을 만들 수 있다.
  3. 함수 이외의 블록 레벨 스코프를 인정하지 않아 엉뚱한 값이 출력될 위험이 있다

 

2. let 키워드는 var 키워드와 어떤 점이 다른가요? 🔥🔥

 

  1. 변수 중복 선언이 불가능하다
  2. let 키워드 역시 호이스팅되지만, TDZ가 존재해 초기화 이전에 접근할 수 없고, 접근하려 한다면 참조 에러 메시지가 뜬다
  3. let 키워드는 블록 레벨 스코프를 사용하기에 지역 변수를 전역 변수로 취급하는 var 키워드의 한계를 개선할 수 있다

 

3. let과 달리 const 키워드만의 특징은 어떤 것이 있나요? 🔥

 

  1. 선언과 동시에 값을 할당해줘야 한다.
  2. 이미 선언된 const 변수에는 값을 재할당해줄 수 없다.
  3. 값을 재할당해줄 수 없기 때문에 상수로 자주 사용된다.
  4. 또한, 원시값을 할당할 경우 값을 변경하기 어려우므로, 참조값을 할당해 객체 내에 값을 바꾸는 식으로 코드를 짜는 것이 좋다.

 

4. TDZ가 무엇인가요? 🔥🔥

 

  • TDZ는 변수 생성 과정에서 선언 단계와 초기화 단계 사이에 존재하는 일시적 사각지대이다. 아직 변수 선언문이 등장하지 않은 시점에서 변수에 접근 가능했던 var 키워드의 한계를 극복하고자 let과 const 키워드부터 적용하였다. 만약 선언문 등장 이전에 변수에 접근하고자 한다면 TDZ로 인해 참조 에러가 뜨게 된다.

 


</> 끊임없이 성장하기 위해 공부한 내용을 글로 작성하고 있습니다. 틀린 부분이나 추가해야 할 부분이 있다면 언제든 댓글로 남겨주세요❗️

 


References

 

728x90
반응형