본문 바로가기

study/JavaScript

Js - 어휘적 환경(Lexical Environment)과 클로저(Closure)

목차

1. 어휘적 환경(Lexical Environment)란?
2. 실행 컨텍스트와 어휘적 환경
3. 어휘적 환경의 작동 방식
4. 클로저란?
5.  클로저 예시

1. 어휘적 환경(Lexical Environment)란?

  • 코드 Block, Function, Script를 실행하기 앞서 생성되는 객체로, 실행할 스코프 범위 안에 있는 변수와 함수를 저장하는 객체이다.
  • 코드를 실행하면서 참조가 필요한 변수의 값을 어휘적 환경 객체에서 식별자 이름을 키로 찾는다.
  • 렉시컬 환경은 환경 레코드(Environment Record)외부 렉시컬 환경(Outer Lexical Environment)에 대한 참조구성되어있다.
  • 환경 레코드
    • 모든 지역 변수를 프로퍼티로 저장하고 있는 객체이다.
    • this 값과 같은 기타 정보도 저장된다.
  • 외부 렉시컬 환경에 대한 참조
    • 코드를 실행할 때 필요한 변수를 해당 지역 스코프의 환경 레코드에서 먼저 찾는다.
    • 만약 찾지 못하였다면 렉시컬 환경이 가지고 있는 외부 렉시컬 환경에 대한 참조로 접근하여 필요한 변수가 외부 환경 레코드에 있는지 찾는다. 이런식으로 글로벌 렉시컬 환경의 환경 레코드까지 찾는다. 만약 찾지 못하였다면 참조 오류가 발생한다.

 

 

2. 실행 컨텍스트와 어휘적 환경

  • 실행 컨텍스트는 현재 실행하고 있는 함수 내의 정보를 가지고있다. (함수의 파라미터, this, 변수 등)
  • 실행하고 있는 함수의 정보를 어휘적 환경에 저장하여, 어휘적 환경은 실행하고 있는 함수의 정보를 가지고 있다.
  • 실행컨텍스트가 어휘적 환경을 관리하며, 어휘적 환경은 실행 컨텍스트에 포함되어 있다.

 

 

3. 어휘적 환경의 작동 방식

  • 스크립트 코드가 시작되면 글로벌 렉시컬 환경 객체가 만들어진다.
  • 코드를 실행하기 전 선언되어 있는 변수와 함수를 글로벌 환경 레코드에 저장한다.
  • var로 선언된 변수
    • 코드를 실행시키기 전에 var로 선언된 변수는 환경 레코드에 변수 이름을 key로, value는 undefind초기화된다.
    • 호이스팅 현상이 일어나는것도 위의 이유 때문이다
  • let, const로 선언된 변수
    • let, const로 선언된 변수는 환경 레코드에 변수 이름을 key로, value는 <uninitialized> 라는 상태로 초기화된다.
    • 위의 이유 때문에 호이스팅이 일어날 경우 let, const로 선언된 변수는 참조 에러가 나타나는것이다.

 

 

4. 클로저란?

  • 함수와 렉시컬 환경의 조합이다.
  • 함수가 생성될 당시의 외부 변수를 기억한다.
  • 생성 이후에도 계속 접근 가능하다.
  • 정리하자면 클로저란 외부 변수를 기억하고 접근할 수 있는 함수이다.

 

5.  클로저 예시

function Counter() {
  let count = 0;
 
  return function() {
    return count = count + 1;
  };
}
 
let devSik = Counter();
let devSik2 = Counter();
let devSik3 = Counter ();

/** 독립된 count 값을 리턴한다. */
devSik(); // 클로저 함수 실행
devSik2();
devSik3();

Counter 함수가 호출될 때, 새로운 렉시컬 환경이 만들어진다. 그리고 Counter 함수를 실행하기 위해 필요한 변수를 렉시컬 환경의 환경레코드에 저장한다. 즉, 환경 레코드에는 count 라는 지역변수가 저장될 것이다.

 

devSik 변수에는 호출되어 만들어진 Counter 함수가 저장되어 있고, devSik을 실행한다면 중접함수의 외부 렉시컬 환경에 대한 참조에 의해 Counter 함수 안에있는 식별자 count 변수의 값이 변하게 된다. 함수가 실행될 때가 아닌 함수가 호출될 때 렉시컬 환경에 외부 렉시컬 환경에 대한 참조를 저장하기 때문이다. 이러한 현상을 클로저라고 한다.

 

만약 Counter 함수가 여러 번 호출돼어 devSik1, devSik2, .... 이런식으로 여러 개의 devSik함수가 생기더라도 이 함수들이 리턴하는 값은 독립적일 것이다. 왜냐하면 Counter 함수가 실행될 때마다 렉시컬 환경이 새롭게 만들어지고, devSik 함수는 그 새롭게 만들어진 렉시컬 환경을 참조할 것이기 때문이다. 즉, Counter함수와 관련된 렉시컬 환경이 여러 생긴다는 것이다.