Posts 모던 자바스크립트 #6.3 변수의 유효범위와 클로저
Post
Cancel

모던 자바스크립트 #6.3 변수의 유효범위와 클로저

모던 JavaScript 튜토리얼을 따라가면서 정리합니다.

자바스크립트는 함수 지향 언어이기 때문에 개발자에게 많은 자유도를 준다. 함수를 동적으로 생성할 수 있고, 생성한 함수를 다른 함수에 인수로 넘길 수 있으며, 생성된 곳이 아닌 곳에서 함수를 호출할 수도 있다.

함수 내부에서 함수 외부에 있는 변수에 접근할 수 있다. 그런데 함수가 생성된 이후에 외부 변수가 변경되면 어떻게 될까? 함수는 새로운 값을 가져올까? 아니면 생성 시점 이전의 값을 가져올까?

코드 블록

코드 블록 {...} 안에서 선언한 변수는 블록 안에서만 사용할 수 있다. 이런 특징은 고유한 작업을 수행하는 코드를 한데 묶는 용도로 활용할 수 있다. if, for, while 등에서도 마찬가지이다. 이런 특징은 변수의 범위를 블록 범위로 한정시킬 수 있어서 유용하다.

중첩 함수

함수 내부에서 선언한 함수를 중첩(nested) 함수라고 부른다. 자바스크립트에서는 중첩 함수를 손쇱게 만들 수 있다. 중접 함수는 코드를 정돈하는 데 사용할 수 있다.

중첩 함수는 반환될 수 있다는 점에서 흥미롭다. 새로운 객체의 프로퍼티 형태로나 중첩 함수 그 자체로 반환된다. 반환된 함수는 어디서든 호출해 사용할 수 있다. 이때도 외부 변수에 접근할 수 있다는 점은 변함 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function makeCounter() {
  let count = 0;
  
  return function() {
    return count++;
  };
}

let counter = makeCounter();

alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2

counter를 여러 개 만들었을 때, 이 함수들은 서로 독립적일까? 함수와 중첩 함수 내 count 변수에는 어떤 값이 할당될까?

렉시컬 환경

단계1. 변수

자바스크립트에서는 실행 중인 함수, 코드 블록, 스크립트 전체는 렉시컬 환경(Lexical Environment)이라 불리는 내부 숨김 연관 객체(internal hidden associated object)를 갖는다.

렉시컬 환경 객체는 두 부분으로 나뉜다.

  • 환경 레코드(Environment Record): 모든 지역 변수를 프로퍼티로 저장하고 있는 객체이다. this 값과 같은 기타 정보도 여기 저장된다.
  • 외부 렉시컬 환경(Outer Lexical Environment)에 대한 참조: 외부 코드와 연관된다.

‘변수’는 특수 내부 객체인 환경 레코드의 프로퍼티일 뿐이다. ‘변수를 가져오거나 변경’하는 것은 ‘환경 레코드의 프로퍼티를 가져오거나 변경’함을 의미한다.

image

스크립트 전체와 관련된 렉시컬 환경은 전역 렉시컬 환경(global Lexical Environment)라고 한다. 위에서 전역 렉시컬 환경은 외부 참조를 갖지 않으므로 화살표가 null을 가리키는 것을 확인할 수 있다. 코드가 실행되고 실행 흐름이 이어져 나가면서 렉시컬 환경은 변화한다.

렉시컬 환경은 자바스크립트가 어떻게 동작하는지 설명하는 데 쓰이는 ‘이론상의’ 객체이다. 따라서 코드를 사용해 직접 렉시컬 환경을 조작하는 것은 불가능하다.

자바스크립트 엔진들은 명세서에 언급된 사항을 준수하면서 엔진 고유의 방법을 사용해 렉시컬 환경을 최적화한다. 사용하지 않는 변수를 버려 메모리를 절약하거나 기타 내부 트릭을 사용한다.

단계2: 함수 선언문

함수는 변수와 마찬가지로 값이다. 다만 함수 선언문(function declaration)으로 선언한 함수는 일반 변수와는 달리 바로 초기화된다.는 차이가 있다.

함수 선언문으로 선언한 함수는 렉시컬 환경이 만들어지는 즉시 사용할 수 있다.

모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다. 함수는 [[Environment]]라는 숨김 프로퍼티를 갖는데, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장된다.

따라서 counter.[[Environment]]에는 {count: 0} 이 있는 렉시컬 환경에 대한 참조가 저장된다. [[Environment]]는 함수가 생성될 때 딱 한번 그 값이 세팅된다. 그리고 이 값은 영원히 변하지 않는다.

counter()를 호출하면 각 호출마다 새로운 렉시컬 환경이 만들어진다. 그리고 이 렉시컬 환경은 counter.[[Environment]]에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조한다.

클로저(closure): 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미한다. 자바스크립트에서는 모든 함수가 자연스럽게 클로저가 된다.

가비지 컬렉션

함수 호출이 끝나면 함수에 대응하는 렉시컬 환경이 메모리에서 제거된다. 함수와 관련된 변수들은 이때 모두 사라진다. 따라서 함수 호출이 끝나면 관련 변수를 참조할 수 없다.

그런데 호출이 끝난 후에도 여전히 도달 가능한 중첩 함수가 있을 수 있다. 이때는 이 중첩 함수의 [[Environment]] 프로퍼티에 외부 함수 렉시컬 환경에 대한 정보가 저장된다. 도달 가능한 상태가 되는 것이다.

This post is licensed under CC BY 4.0 by the author.

시뮬레이션(Simulation): 숫자게임

정규표현식: 전역 플래그(/g)와 test()

Loading comments from Disqus ...