본문 바로가기

CS/Javascript

자바스크립트 렉시컬 환경이란?

반응형

 

오늘은 렉시컬 환경에 대해 서치 한 내용을 작성해 보자. 전에는 몰랐는데 렉시컬 환경과 실행 컨텍스트, 호이스팅이 전부 밀접한 관계가 있다는 것을 깨달았다! 전부 다 따로따로 이뤄진 개념인 줄 알았더니.. 과장 보태서 신세계를 경험한 것과 같은 충격이었다.

나는 이 글을 정독하였고, 기술면접 및 기록을 위해 해당 블로그의 내용을 참고 및 요약하여 남기려고 한다.

https://developer-alle.tistory.com/407

 

[JavaScript] 렉시컬 환경

스코프랑 클로저에 대해 전에도 정리했었지만, 이번엔 렉시컬 환경이라는 개념과 함께 정리하려고 한다. Lexical Environment의 개념 Lexical Environment는 코드 block, function, script를 실행하기 앞서 생성되

developer-alle.tistory.com

 

렉시컬 환경

특정 코드가 작성, 선언된 환경(장소)을 의미한다. 코드 block, function, script를 실행하기 앞서 생성되는 특별한 객체로, 실행할 스코프 범위 안에 있는 변수함수프로퍼티로 저장하는 객체이다. 

렉시컬 환경에는 총 두 가지, 글로벌 렉시컬 환경외부 렉시컬 환경이 있다.

 

렉시컬 함수와 실행 컨텍스트(execution context)와의 관계

실행 콘텍스트란 코드들이 실행되기 위한 환경이다. 현재 실행하고 있는 함수 내의 현재 변수 상태와 this의 값 등을 저장하고 있고, 현재 실행 중인 line을 기억하고 있어 nested function이 호출될 때, 이미 실행하고 있는 정보를 저장해 뒀기 때문에 nested function의 실행이 끝나면 다시 돌아올 수 있는 것이다.

실행 컨텍스트는 소스 코드를 실행하기 위해 필요한 것들을 관리하는 내부 메커니즘이고, 실행 컨텍스트가 렉시컬 환경을 관리하고 있다고 한다.  실행 컨텍스트에는 현재 실행하고 있는 함수 내 변수를 저장하고 있다고 하니, 이를 렉시컬 환경이라는 객체에 저장해두고 변경이 있을 때마다 업데이트하고 필요할 때 꺼내 쓰는 것이다.

 

글로벌 렉시컬 환경 

전역 코드가 시작되면 글로벌 렉시컬 환경 객체가 만들어지고, 코드를 실행하기 전에 선언되어 있는 변수와 함수를 먼저 글로벌 환경 레코드에 저장한다.

변수 선언 방식에 따른 차이(+호이스팅)

var로 선언된 변수는 환경 레코드에 변수 이름을 key로, undefined를 value로 하여 초기화된다. let, const로 선언된 경우에는 환경 레코드에 변수 이름을 key로, <uninitialized>라는 상태를 value로 초기화한다. 그래서 이 변수가 선언된 줄에 도달하기 전에 변수를 참조하고자 한다면, var와 달리 ReferenceError가 발생한다. 이 개념을 이해함으로써 비로소 호이스팅에 대해 정확하게 이해할 수 있었다.

함수가 선언식으로 선언된 경우 var로 선언된 변수와 달리 함수 선언식은 함수 이름을 key로 하고 함수 자체를 value로 저장하여 완전하게 초기화된다. var로 선언된 변수는 선언 줄 전에 접근하면 초기화 값인 undefined를 리턴하는 반면, 함수 선언식은 초기화될 때 완전하게 저장되므로 선언 줄 이전에 접근하더라도 의도대로 해당 함수를 사용할 수 있다.

 

외부 렉시컬 환경(+클로저)

코드를 실행할 때 필요한 변수를 해당 로컬 스코프와 연관돼 있는 환경 레코드에서 먼저 찾은 후에 찾지 못하였다면 렉시컬 환경이 갖고 있는 외부 렉시컬 환경에 대한 참조로 접근하여 찾는다. 이 과정을 반복하며 글로벌까지 도달하여 식별자를 검색하고, 없다면 에러가 발생하는 것(use strict 모드일 때만)이다.

아래의 예시를 살펴보자.

function makeCounter() {
  let count = 0;
 
  return function() {
    return count++;
  };
}
 
let counter = makeCounter();

makeCounter라는 함수의 호출이 시작될 때, 새로운 렉시컬 환경이 만들어진다. 거기에 makeCounter 함수 실행에 필요한 count라는 로컬 변수가 저장된 후, return 문 뒤의 함수가 counter 변수(return function()~ 이 저장되어 있어 counter은 함수임)에 저장된다.

counter 함수를 실행하면 makeCounter에 있었던 count 변수를 참조하여 0이라는 값이 리턴된다(0인 이유는 후위 연산자를 사용했기 때문. ++count면 1이 나옴). 여기서 클로저(외부 변수를 기억하고 접근할 수 있는 함수)에 대한 개념이 나온다. 

모든 함수는 [[Environment]] 라는 내부 프로퍼티를 갖고 있다. 이 프로퍼티는 함수가 만들어질 때함수를 둘러싼 외부 렉시컬 환경에 대한 참조를 저장한다. makeCounter가 실행되면서 counter 함수는 만들어지고 이 때 makeCounter의 렉시컬 환경에 대한 참조가 counter.[[Enviroment]] 프로퍼티에 저장된다. 그리고 counter 함수가 실행될 때, 비로소 counter 함수의 렉시컬 환경 객체가 생성되고, 이 객체가 외부 렉시컬 환경에 대한 참조 counter.[[Enviroment]] 프로퍼티로부터 가져온다. 이러한 과정으로 인해 counter 함수가 언제 어디서 실행되든 count 변수를 참조할 수 있는 것이다.

반응형