var 모르고 쓰면 큰일납니다.

2023.07.13
10분
댓글

글을 시작하며

안녕하세요! Javascript 개념을 조금 더 쉽고 재밌게 풀어보기 위해 시리즈 글을 쓰게 되었습니다.
첫 포스팅이다보니 부담감을 이기지 못하고 (...) 주목을 끌기 위한 제목을 짓게 되었습니다. 😂

하지만 var 키워드는 실제로 여러가지 Side Effect를 유발할 수 있는 키워드입니다.
그렇기 때문에 ES6 버전부터는 let, const 키워드가 var를 대체하게 되었습니다.

그럼 지금부터 var의 특성에 대해 알아보고 이 녀석이 어떠한 문제를 발생시키는지 함께 알아보겠습니다!

태초에는 var가 있었으니..

ES5까지 var는 자바스크립트에서 변수를 선언할 수 있는 유일한 키워드였습니다.
하지만 var에 어떠한 문제들이 있었는지 ES6에서 let과 const라는 키워드가 등장했습니다.

💡 대개 새로운 무언가가 만들어지는 이유는 이전의 문제점을 개선하기 위해서입니다!

그렇다면 var의 특징과 단점들에 대해서 알아보고, 이를 let과 const가 어떻게 개선했을지 유추해봅시다.


JavaScript의 특징

1. 암묵적 undefined 초기화

var score;

변수를 선언만 하고 값을 할당하지 않은 상태입니다.
따라서 변수 선언에 의해 할당된 메모리 공간은 비어 있을 것이라고 생각할 수 있습니다.
하지만... 우리의 JS는 undefined라는 값을 할당하여 초기화합니다!

변수에 값을 할당하지 않으면 바로 에러가 발생하는 Java, Python과 다르게 친절해 보일 수도 있습니다.

여기서 퀴즈 하나 풀어보겠습니다!

Q. 변수 선언 시 메모리를 확보한 다음, 값을 할당하지 않은 상태에서 참조하면 어떤 일이 발생하게 될까요?

A. 쓰레기 값을 참조하는 에러가 발생할 수 있습니다.


그렇다면 변수를 undefined로 초기화하는 var는 쓰레기 값을 참조하는 에러가 발생하지 않겠군요?

암묵적 초기화의 장단점

  • 👍 장점: var는 암묵적 초기화를 수행하므로 쓰레기 값 참조의 위험으로부터 안전합니다.
  • 👎 단점: undefined 참조 에러가 발생할 수 있습니다.

(사실상 이러나 저러나 에러가 발생하네요.. 🤯)


2. 변수 선언의 실행 시점과 호이스팅

// 문제! score는 어떤 값을 가질까요?
console.log(score);
var score;

// 정답: undefined

지금까지 Java나 Python에 익숙했던 저에게는 가장 충격적이었던 JS의 특징이었습니다.
감히 선언 전에 읽으려고 하다니.. 🤷‍♀️

이러한 동작이 가능한 이유를 살짝 어려운 정의로 설명해보겠습니다.

JS 엔진은 모든 선언문을 찾아서 런타임 이전 단계(코드 실행 전)에서 먼저 실행하고
코드를 순차적으로 실행합니다.

쉽게 이해해보자면, JS 엔진은 코드를 실행하기 전에 모든 변수들을 찾아서 미리 메모리 공간을 할당해버립니다.
얼마나 친절하냐면 var로 선언한 경우엔 undefined로 초기화까지 시켜줍니다.

그렇기 때문에 선언 전에 변수를 참조해도 참조 에러가 발생하지 않고 undefined가 출력됩니다.

이러한 개념을 호이스팅이라고 합니다.
JS 면접 단골 문제이지만 추가적인 개념이 필요하기 때문에 다음 시간에 다시 배워보도록 하겠습니다!


3. 값의 할당

// 문제입니다!
console.log(score); // undefined (이건 이제 껌이쥬)
score = 80;
var score;
console.log(score); // 그렇다면 이 값은 무엇일까요?

JS가 주언어가 아닌 분들이 보시면 이게 무슨 말도 안되는 코드인가 싶으실 수도 있습니다.
보통 언어들은 선언 후 값을 할당한 다음에 참조를 하니까요.

하지만 JS가 이 어려운걸 해내네요.

정답은 80!

왜 그럴까요? 위의 내용을 복습해보면 이해할 수 있습니다.

1. 변수 선언은 런타임 이전 단계에서 실행됩니다.

  • 즉, 코드에 있는 모든 변수는 상단에 선언된 것이나 마찬가지입니다.

2. 소스 코드가 실행되는 시점에 값의 할당이 일어납니다.

// 아하 그러면 여기에 var score;가 선언된거나 마찬가지군요!
console.log(score); // undefined (JS의 암묵적 초기화)
score = 80; // score는 이미 선언 되었으므로 초기화가 가능합니다.
var score;
console.log(score); // 80!

4. 함수 레벨 스코프

var x = 1; // 전역 변수
if (true) {
  // x는 코드 블록 내에서 재선언해도 지역 변수가 아닌 전역 변수로 선언됩니다.
  // 이는 의도치 않게 변수값이 변경되는 부작용이 있습니다.
  var x = 10;
}
console.log(x); // 10
  • var 키워드로 선언한 변수는 함수의 코드 블록만을 지역 스코프로 인정합니다.
  • 쉽게 이해하자면, 함수 내부가 아니라면 변수들은 모두 전역 변수가 됩니다.
  • 위의 코드에서도 var를 코드 블록 안에서 선언했는데도 전역 변수가 되어서 x의 값이 변경 된 것을 볼 수 있습니다.

❓ 이게 과연 최선일까요?

의문 1.

  • 다른 언어들이 선언과 초기화와 관련된 일정 규칙을 따르는 이유가 있을텐데, 이대로 사용해도 괜찮을까요?

의문 2.

  • 호이스팅 되니까 변수들을 상단이 아니라 아무 곳에서나 선언해도 괜찮겠네요!
  • 하지만 코드 흐름이 이상하지 않나요?

의문 3.

  • 모든 선언문은 런타임 이전에 실행하는데 선언문이 많으면 어떻게 되는거죠?
  • 느려지지 않을까요?

의문 4.

  • 변수 명이 겹치는 경우, 지역 변수 개념이 사라지는게 아닐까요?

이러한 문제를 개선하기 위해 let과 const가 등장했습니다.

😲 var에 어떤 특징이 있었죠?

  1. 선언 후 자동으로 undefined로 초기화
  2. 호이스팅
  3. 함수 레벨 스코프

☠️ 발생할 수 있는 문제

  1. undefined 참조 에러가 발생할 수 있습니다.
  2. 선언문이 많아지면 호이스팅에 의해 JS 구동이 지연될 수 있으며 코드 가독성이 낮아집니다.
  3. 지역 변수와 변수명이 겹치면 전역 변수를 변경하게 됩니다.

그래서 어떻게 해결했나요?

그것은 다음 포스팅에서 밝혀집니다..!