앞서

이 글은 https://ko.javascript.info/var

[

오래된 'var'

ko.javascript.info

](https://ko.javascript.info/var)

이 링크의 글을 기반으로 작성하고 있습니다.

Javascript 의 변수 선언

Javascript 를 처음 시작하는 분들이라면, 사실 굳이 몰라도 되는 개념이 바로 var 이다. 하지만 언젠가는 알아야 한다. 다른 사람의 코드를 봐야 할 일이 분명히 생기고, 레거시 코드를 다루면서 let 이나 const 로 고쳤을 때 예기치 못한 에러가 발생할 수도 있다. 그러므로 이 글에서 Javascript 의 변수 선언식 중 가장 오래 되었고, 가장 관대한 변수 선언식인 var 를 알아보자.

Javascript 에는 세 가지의 변수 선언 방식이 있다.

  1. let
  2. const
  3. var

최신 Javascript 문법으로 배운 사람들은 let 과 const만 사용하라는 말을 많이 봤을 것이다. let 은 일반 변수를 선언할 때, const 는 변하지 않는 값을 나타낼 때 사용한다고 말이다. 하지만 오래된 코드를 보다보면 var 가 많이 쓰이는 데, 통상적으로 var 는 let 으로 바꿔도 크게 문제가 발생하지 않는다.

하지만 var 는 초기 자바스크립트 구현 방식 때문에 let 과 const 로 선언한 변수와는 다른 방식으로 동작한다. 어떻게 다른지 한번 보자.

var 는 블록 스코프가 없다

var 로 선언한 변수의 스코프는 함수 스코프이거나 전역 스코프이다. 블록 기준으로 스코프가 생기지 않기 때문에, 블록 안에서 선언된 변수를 블록 밖에서 접근 가능하다.

if (true) {
  var test = true; // 'let' 대신 'var'를 사용했습니다.
}

alert(test); // true(if 문이 끝났어도 변수에 여전히 접근할 수 있음)

반복문에서도 같은 일이 일어난다.

for (var i = 0; i < 10; i++) {
  // ...
}

alert(i); // 10, 반복문이 종료되었지만 'i'는 전역 변수이므로 여전히 접근 가능합니다.

var 가 함수 안에 있다면, 함수 스코프에 있는 변수가 된다.

function sayHi() {
  if (true) {
    var phrase = "Hello";
  }

  alert(phrase); // 제대로 출력됩니다.
}

sayHi();
alert(phrase); // Error: phrase is not defined

오래 전의 자바스크립트에서는 블록 수준의 렉시컬 환경이 만들어 지지 않았기 때문이라고 한다.

var 와 let 의 차이점 : var 는 재선언이 된다.

만약 let 으로 이미 선언된 변수를 다시 선언하면, 에러가 발생한다.

let user;
let user; // SyntaxError: 'user' has already been declared

반면에 var 로 선언된 변수는 몇 번을 재선언해도 상관없다.

만약에 var 로 선언된 변수를 다시 var 로 재선언하게 되면, var 라는 키워드는 무시당한다.

var user = "Pete";

var user = "John"; // this "var" does nothing (already declared)
// ...it doesn't trigger an error

alert(user); // John

호이스팅

var 선언은 함수가 시작될 때 처리된다. 전역에서 선언한 var 변수라면, 스크립트가 시작될 때 처리된다;.

함수 본문 내에서 var 로 선언한 변수는 선언 위치와 상관없이, 함수 본문이 시작되는 지점에서 정의된다.

function sayHi() {
  phrase = "Hello";

  alert(phrase);

  var phrase;
}
sayHi();

이런 함수가 있다면, 암묵적으로 var phrase 가 함수가 시작될 때 처리된다.

function sayHi() {
  var phrase;

  phrase = "Hello";

  alert(phrase);
}
sayHi();

블록 스코프는 무시되기 때문에, 아래 코드 역시 위처럼 동작한다.

function sayHi() {
  phrase = "Hello"; // (*)

  if (false) {
    var phrase;
  }

  alert(phrase);
}
sayHi();

이렇게 변수가 끌어올려 지는 현상을 호이스팅 이라고 한다.

위 함수에서, if (false) 블록 안의 코드는 절대 실행되지 않지만, 호이스팅에는 전혀 영향을 주지 않는다. if 블록 내부의 var 는 sayHi 함수의 시작 부분에서 처리되기 때문에, 이미 (*) 부분에서 phrase 는 선언이 되어 있는 상태이다.

그리고 주의할 것이 있다.

선언은 호이스팅 되지만, 할당은 호이스팅 되지 않는다.

예시를 살펴보자.

function sayHi() {
  alert(phrase);

  var phrase = "Hello";
}

sayHi();

var phrase = "Hello" 행에선 두 가지 일(변수의 선언과 할당)이 일어난다.

변수 선언은 함수 실행이 시작될 때 처리되지만(호이스팅) 할당은 호이스팅되지 않기 때문에, 위 코드는 아래처럼 동작한다.

function sayHi() {
  var phrase; // 선언은 함수 시작 시 처리됩니다.

  alert(phrase); // undefined

  phrase = "Hello"; // 할당은 실행 흐름이 해당 코드에 도달했을 때 처리됩니다.
}

sayHi();

이처럼 모든 var 선언은 함수 시작 시(전역일 경우 스크립트 시작 시 ) 처리되기 때문에, var 로 선언한 변수는 어디서든 참조할 수 있다. 값을 할당하기 전에는 undefined 가 뜨지만 말이다.(할당은 호이스팅 되지 않기 때문이다)

 

즉시 실행 함수 표현식 ( IIFE )

지금이야 let 과 const 라는 선택지가 있었지만, 옛날에는 정말 선택지가 var 뿐이었다. 그런데 var 는 블록 스코프를 가질 수가 없다. 개발자들은 var 도 블록 스코프를 가질 수 있게 여러가지 방안을 고려하게 되었고, 이 때 만들어진 것이 즉시 실행 함수 표현식(Immediately-invocked function expressions) 이다. 줄여서 IIFE 라고도 한다. (개인적으로 면접 질문으로 받아봤다)

 

요즘에는 당연히 쓸 일이 없다. let 과 const 가 대놓고 권장되고 있기 때문에. 하지만 오래된 코드를 보다보면 만날 수 있기 때문에 뭔지 알아 둘 필요가 있다.

 

IIFE는 다음과 같이 생겼다.

 

(function() {

  let message = "Hello";

  alert(message); // Hello

})();

함수를 함수 표현식으로 만든 다음에, 바로 호출해버리는 것이다.

즉시 실행 함수를 만들 때는, 함수 표현식을 괄호로 둘러쌓아 ( function() {...} ) 의 형태로 만들어 줘야 한다. (안하면 에러가 난다)

 

자바스크립트는 function 키워드를 만나는 순간, 함수 선언문이 시작될 것이라 예상한다. 근데 함수 선언문으로 함수를 만들 때는 함수의 이름이 반드시 있어야 한다. 그래서 아래처럼 하면 에러가 난다.

 

// 함수를 선언과 동시에 실행하려고 함
function() { // <-- Error: Function statements require a function name

  let message = "Hello";

  alert(message); // Hello

}();

 "그럼 이름 넣으면 되는 거 아닌가?" 라고 생각해서 넣어봐도 에러가 발생한다. 자바스크립트는 함수 선언문으로 정의한 함수를 정의와 동시에 바로 호출하는 것을 허용하지 않기 때문이다.

 

// 맨 아래의 괄호 때문에 문법 에러가 발생합니다.
function go() {

}(); // <-- 함수 선언문은 선언 즉시 호출할 수 없습니다.

하지만 함수를 괄호로 감싸면, 자바스크립트가 함수를 함수 선언문이 아닌 표현식으로 인식하도록 속일 수 있다. 함수 표현식은 이름이 없어도 괜찮고, 즉시 호출도 가능하다.

 

괄호를 사용하는 방법 말고도, 자바스크립트가 함수 표현식이라고 인식하게 해주는 다른 방법들이 있다.

 

// IIFE를 만드는 방법

(function() {
  alert("함수를 괄호로 둘러싸기");
})();

(function() {
  alert("전체를 괄호로 둘러싸기");
}());

!function() {
  alert("표현식 앞에 비트 NOT 연산자 붙이기");
}();

+function() {
  alert("표현식 앞에 단항 덧셈 연산자 붙이기");
}();

물론 모던 자바스크립트에서는 별 필요 없다. 알아만 두자!

 

요약

var 로 선언한 변수는 let 과 const 로 선언한 변수와 다른 두 가지 주요한 특성을 보인다.

  1. var 로 선언한 변수는 블록 스코프가 아닌 함수 스코프를 갖는다.
  2. var 선언은 함수가 시작되는 시점(전역에선 스크립트 시작 시점)에서 처리된다. (호이스팅)

이 특성들은 대부분의 상황에서 부작용을 만들어 내고, let 이 표준으로 도입된 이유가 바로 이것이다. 변수는 블록 레벨 스코프를 갖는 게 좋기 때문이다. 이것이 let 과 first 가 대세가 된 이유이다.

 

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기