읽는 데 5분 26. December 2020
자바스크립트 유효 범위와 var

자바스크립트 변수의 유효 범위

자바스크립트에서 변수를 선언할 때 사용하는 키워드로 var, let, const 세 가지가 있다. var는 뒤에 두 개의 키워드와 다른 점이 존재하는데 letconstES6에 이르러 만들어진 표준안이므로 ES6를 지원하는 환경에서만 사용할 수 있다. (IE 같은 환경만 아니면 사용이 가능하다) letconst는 여타 다른 언어와 비슷한 블록 기준으로 범위가 생기는데 var는 블록 기준으로 범위가 생성되지 않는다.

이제 사용하면 안되는 var

자바스크립트를 이제 배우는 시점에서 var 키워드를 사용해서 변수를 선언하세요. 라고 가르침을 받았다면 나중에 배울 letconst를 사용할 때 혼란스러울 수 있다. 그리고 현재로써 var를 사용해야할 이유가 전혀 없다.

하지만 오래된 자바스크립트 코드를 수정해야 할 일이 반드시 있을 것이고, 모든 자바스크립트 코드가 표준 스펙이 업데이트 됨에 따라 자동으로 수정이 되지 않기 때문에 반드시 직접 수정해야 할 일이 생길 것이다. 그럴 때 var의 자세한 특성을 모른다면 코드 수정에 난항이 생길 수 있다.

범위

var가 가지는 범위는 단 2가지다. 전역적으로 범위를 가지거나 함수에서 범위를 가진다.

var globalScope = 1 // 전역 범위

function helloWorld() {
  var functionScope = 1 // 함수 범위
}

한 파일을 기준으로 globalScope는 전역 범위이므로 어떤 함수 혹은 어떤 블록에서나 호출할 수 있다. 다만 functionScope 변수는 함수 범위에 선언됬으므로 함수 바깥에서 참조하는 것은 불가능하다.

if (true) {
  var blockScope = 'block'
}

console.log(blockScope)
// block

var는 오직 전역/함수 범위만 가지기 때문에 블록 안에 선언됬더라도 블록 외부에서 사용이 가능하다. varlet으로 바꾸면 blockScope는 정의되지 않았다는 오류가 뜬다.

호이스팅

function helloWorld() {
  console.log(fScope)
  var fScope = '123456'
}

helloWorld()
// undefined

위 코드는 정상적으로 실행된다. 출력 값은 undefined이다. 바로 호이스팅이 일어나기 때문이다. var 키워드로 선언된 변수가 범위 내에 존재한다면 그 선언을 범위의 최상단으로 끌어 올린다. 따라서 위 코드와 아래 코드는 같다.

function helloWorld() {
  var fScope
  console.log(fScope)
  fScope = '123456'
}

helloWorld()
// undefined

맨 위 코드를 var에서 let으로 바꾸면 호이스팅이 일어나지 않기 때문에 오류가 나게 된다.

부작용

위의 특성들이 괜찮다고 느껴질 수도 있다. 하지만 이러한 특성이 때로는 부작용을 일으킨다. 이러한 부작용들 때문에 letconst가 등장했으므로 그 키워드들을 사용하면 된다.

클로저

함수형 프로그래밍 언어는 함수를 일급 객체로 취급한다. 이런 언어들의 특성 중 클로저라는 특성이 있다.

function WrapClosure() {
  let counter = 0

  return function() {
    counter++
    console.log(counter)
  }
}

const addCounter = WrapClosure()

addCounter() // 1
addCounter() // 2

WrapClosure 함수는 함수를 반환하는 함수이다. 함수 내부 범위에 선언된 변수 counter는 블록 범위이기 때문에(내부적으로는 자바스크립트 엔진이 여러 범위를 바인딩) 반환하는 함수 안에서 접근할 수 있다. 반환된 함수는 counter를 1 증가 시키고 콘솔에 찍는 간단한 함수이다.

이러한 일이 가능하기 까지 매우 많은 내부 절차가 있지만 단순히 중요하게 봐야 될 부분은 반환된 함수를 감싸는 함수(외부 함수)는 호출이 완료되고 실행 컨텍스트에서 반환되고 소멸되지만 클로저(반환된 함수)에서 외부 변수를 참조한다면 그 변수는 유효하다는 부분이다. 즉 외부 함수의 변수 혹은 객체가 내부 함수에서 사용되고 있다면 그 변수와 객체들은 함수의 호출이 끝나도 존재한다는 뜻이다. 클로저가 이러한 환경을 기억하고 있기 때문이다.

클로저는 외부 함수의 환경을 기억하고 있기 때문에 메모리를 사용하는 입장에서는 부담이 될 수 있다.

클로저로 할 수 있는 것

상태를 유지해야 하거나, 정보 은닉이 필요하거나할 때 유용하게 사용할 수 있다.