JavaScript 스코프, 렉시컬 환경, 실행 컨텍스트 개념 요약 정리

작성 : 2023-11-01수정 : 2023-11-01

목차 펼치기

스코프 (Scope)

코드 내에서 변수에 접근할 수 있는 유효 범위를 정의한다. 식별자를 검색할 때 사용하는 규칙으로 볼 수도 있다. 함수 내부에 함수가 선언될 수 있는 것처럼 스코프는 다른 스코프에 포함될 수 있다.


스코프 체인(Scope Chaine)

모든 스코프는 하나의 계층 구조로 연결되어 최상위의 전역 스코프와 연결된다. 즉, 렉시컬 환경이 연결되어 있는 것이다. 하위 스코프에서는 외부 렉시컬 환경 참조를 통해 상위 스코프로 접근할 수 있는데, 이를 스코프 체인이라고 한다.

여러 스코프에서 동일한 식별자를 선언하는 경우, 스코프 체인에서 가장 먼저 발견된 식별자에 접근하게 된다.

중첩 함수에서는 브라우저 자체에서 메모리 최적화를 위해 상위 스코프의 변수 중 사용하고 있는 변수만 참조하며, 클로저에서는 이렇게 참조하고 있는 변수를 자유 변수(Free Variable)라고 한다.


전역 스코프(Global Scope)
  • 코드의 최상위 범위로, 프로그램 전체에서 접근해 사용할 수 있다.

  • 이 범위에 선언된 변수와 함수를 ‘전역 변수’와 ‘전역 함수’라고 한다.

  • 코드 안정성을 위해 전역 스코프 사용은 최소화 하는 것이 좋다.


지역 스코프(Local Scope)
  • 함수 또는 특정 코드 블록에서만 접근해 사용할 수 있다.

  • 해당 스코프와 하위 스코프에서만 접근할 수 있으며, 외부에서는 접근할 수 없다.

  • 이 범위에 선언된 변수와 함수를 ‘지역 변수’와 ‘지역 함수’라고 한다.

  • 함수 내에서 선언된 변수의 유효 범위를 ‘함수 스코프’라고 한다.

  • {}

    내에서 선언된 변수의 유효 범위를 ‘블록 스코프’라고 한다.


var

키워드는 함수 레벨 스코프로, 함수를 통해서만 지역 스코프를 생성한다. 함수 외에

if

,

for

,

while

,

try~catch

와 같은 블록 내부에서 선언된 경우에는 모두 전역 스코프에 포함된다.


let

,

const

키워드는 블록 레벨 스코프로, 함수 뿐만 아니라 블록 단위에서도 지역 스코프를 생성한다.



정적 스코프(렉시컬 스코프)

함수가 정의되는 시점에 상위 스코프를 결정하는 방식으로 호출 위치는 어떤 영향도 주지 않는다. JavaScript를 비롯해 대부분의 프로그래밍 언어가 이 방식을 따르고 있다.


동적 스코프

함수가 호출되는 시점에 동적으로 상위 스코프를 결정하는 방식이다.



렉시컬 환경 (Lexical Environment)

환경 레코드

외부 렉시컬 환경 참조

로 구성되어 있으며 스코프 내의 변수와 함수를 정의하고 관리하고 상위 컨텍스트에 대한 참조를 가지고 있다.


스크립트 전체와 관련된 환경은 전역 렉시컬 환경(Global Lexical Environment)이라고 부른다.


환경 레코드

(Environment Record)

실행 스코프 안에 있는 식별자 정보들을 관리하는 객체로 JavaScript 엔진은 코드를 실행하기 전에 먼저 렉시컬 환경을 구성하며 환경 레코드에 식별자 정보들을 수집한다. 이 과정을 통해 해당 컨텍스트 내부의 변수명들을 모두 알고 있게 되는데, 이 동작을 추상화한 개념이 바로 ‘호이스팅(Hoisting)’이다.


Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code.


-

ECMAscript

  • 전역 환경 레코드(GER : Global Environment Record)

    • 전역 객체 등 최상위 스코프에 대한 바인딩을 제공한다.

    • ES6 이후

      var

      전역 변수와

      let

      ,

      const

      전역 변수를 구분하기 위해 객체 환경 레코드와 선언적 환경 레코드로 구성되기 시작했다.

      • let

        ,

        const

        식별자는 전역 객체의 프로퍼티가 되지 않고 개념적인 블록 내에 존재하게 된다.


  • 객체 환경 레코드(OER : Object Environment Record)

    • var

      전역변수, 선언문 전역함수, 빌트인 전역 프로퍼티, 빌트인 전역함수, 빌트인 객체를 관리한다.

    • 전역 객체의 프로퍼티와 메소드가 된다.

      • 이 변수는 전역 객체에

        delete

        를 통해 지울 수 없다.

    • 전역 객체 또는

      with

      블럭에서 사용된다.


  • 선언적 환경 레코드(DER : Declarative Environment Record)

    • let

      ,

      const

      식별자들의 바인딩을 관리한다.

    • 함수 환경 레코드(Function Environment Record)

      • 매개변수, arguments, 내부 지역 변수 및 중첩 함수를 관리한다.

    • 모듈 환경 레코드(Module Environment Record)

      • 모듈의 외부 스코프를 나타낸다.


외부 렉시컬 환경 참조(Outer Lexical Environment Reference)

현재 렉시컬 환경과 연결된 상위 렉시컬 환경을 참조하는 링크다. 함수가 호출된 위치가 아닌 정의된 위치에 따라 상위 스코프를 결정한다. 이를 통해 외부 스코프를 참조하는 스코프 체인이 동작한다.



실행 컨텍스트 (Execution Context)

실행할 코드에 제공할 환경 정보들을 저장하고 있는 객체로 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다고 볼 수 있다.


  • 실행 컨텍스트가 활성화 될 때 JavaScript 엔진은 실행에 필요한 환경 정보들을 수집한다.

  • 개발자가 직접 접근하거나 조작할 수 없다.


실행 컨텍스트 스택(Execution Context Stack)을 통해 실행 컨텍스트를 관리하고 실행 순서를 보장한다.


렉시컬 환경을 통해 스코프와 식별자를 관리한다.


구조

javascript
1ExecutionContext = {
2	VariableEnvironment: {
3		EnvironmentRecord: {
4			...variables,
5			...functions,
6		},
7		outer: <GlobalLexitcalEnvironment | OuterLexicalEnvironment | undefined>,
8	},
9	LexicalEnvironment: {
10		EnvironmentRecord: {
11			...variables,
12			...functions,
13		},
14		outer: <GlobalLexitcalEnvironment | OuterLexicalEnvironment | undefined,
15	},
16	ThisBinding: <GlobalThisValue | ThisValue | undefined>
17}

실행 컨텍스트를 객체로 구조화해보면 이럴 것 같다.

  • Variable Environment

    • 선언 지점의 렉시컬환경에 대한 스냅샷으로 생성된 값을 유지한다.

    • 이를 복사하여 Lexical Environment를 생성한다.

  • Lexical Environment

    • 최초 생성 시에는 Variable Environment와 동일하지만 코드 진행에 따라 변경된다.

  • this Binding

    • 현재 실행 컨텍스트에 대한

      this

      를 저장한다.

    • 함수 호출 패턴에 의해 다른 값이 저장된다.

      • 함수가 객체의 메소드로 사용될 때는 해당 객체로 바인딩된다.

      • 함수를 호출하는 주체가 명확하지 않을 경우 전역 객체로 설정된다.

        • strict 모드에서는

          undefined

          로 설정된다.


평가, 실행, 소멸

JavaScript 엔진은 소스 코드를 평가-실행의 2단계로 처리한다. 평가 과정에서 선언문을 실행하여 렉시컬 환경의 환경 레코드에 등록한다. 이후 코드가 실행되면서 함수가 호출되면, 전역 코드의 실행이 중단되고 함수 코드의 평가 및 실행 단계가 실행된다.


실행이 종료되고, 어디에서도 참조되지 않을 경우 GC에 의해 소멸하게 된다. 클로저에서 참조되고 있는 자유 변수의 경우 실행이 종료되어도 참조 카운트가 존재하기 때문에 GC를 통해 메모리에서 지워지지 않는다. 이를 해제 해주려면 명시적으로 기본형 데이터(ex,

null

)를 할당해서 참조를 끊어주어야 한다.


  • 평가

    1. 실행 컨텍스트 생성

    2. this 바인딩

    3. 변수, 함수 등의 선언문 실행

    4. 렉시컬 환경에 식별자 바인딩

  • 실행

    1. 코드 순차 실행 (인터프리터)

    2. 실행 컨텍스트에 실행 결과 반영


종류

ECMAScript 사양의 4가지 소스 코드(전역 코드, 함수 코드, eval 코드, 모듈 코드)가 실행 컨텍스트를 생성한다.


  • 전역 실행 컨텍스트(Global Execution Context)

    • 처음

      script

      코드가 실행될 때 자동으로 생성된다.

    • 변수 객체를 생성하는 대신 JavaScript 런타임 환경이 제공하는 전역 객체를 사용한다.

      • 내장 객체가 아닌 호스트 객체를 사용하는 것인데, 브라우저 환경에서는

        window

        , Node.js 환경에서는

        global

        객체를 사용한다.

    • 전역 변수나 전역 함수 등의 전역 스코프를 관리한다.

      • var

        키워드와 함수 선언문이 여기에 포함된다.

    • this

      를 전역 객체로 설정한다.

  • 함수 실행 컨텍스트(Function Execution Context)

    • 함수가 호출될 때마다 해당 함수의 실행 컨텍스트로 생성되어 콜 스택에 푸시된다.

    • 함수 내부의 지역 변수, 매개변수, arguments 객체를 관리한다.

    • 생성한 지역 스코프를 스코프 체인으로 연결한다.

  • Eval 실행 컨텍스트(Eval Execution Context)

    • eval()

      함수를 사용할 때 생성되어 push 된다.

    • strice mode에서 독자적인 스코프를 생성한다.

    • 보안과 성능 이슈로 인해 MDN에서

      사용하지 말 것

      을 경고하고 있다.

  • 모듈 실행 컨텍스트(Module Execution Context)

    • ES6 부터 도입된 모듈로 인해 추가되었다.

    • 모듈 스코프를 생성하여 모듈 단위로 코드를 구성할 수 있다.

    • 모듈의 실행이 끝나면 파기된다.




Wanna get in touch?

All Icons byiconiFy