본문 바로가기
프론트엔드 관련/기초

Execution Context를 통한 Closure의 이해

by ash9river 2025. 12. 11.
ExecutionContext {
    GlobalExecutioncontext
    FunctionalExecutionContext
    EvalFunctionExecutionContext // 보안에 문제있어서 이젠 거의 안씀
}

 

 

Global Execution Context
 ├─ LexicalEnvironment
 │     ├─ EnvironmentRecord (GlobalDeclarative + GlobalObject)
 │     └─ Outer = null
 ├─ VariableEnvironment
 └─ ThisBinding = global object (browser=window, node=global)

 

Function Execution Context
 ├─ LexicalEnvironment (FunctionEnvironmentRecord)
 │     ├─ EnvironmentRecord (Declarative)
 │     │     매개변수, let/const/var, 함수 선언 저장
 │     └─ Outer = 함수 선언 위치의 Environment
 ├─ VariableEnvironment (거의 동일한 기록용 Env)
 └─ ThisBinding (call/apply/bind 또는 default undefined)

 

함수 호출시 내부변수는  lexicalEnvironment의 Envrecord에 저장

EC는 사라지지만 EnvironmentRecord는 클로저에 의해 살아남을 수 있음

클로저의 본질: FunctionObject.[[Environment]]에 이 EnvironmentRecord가 embed되는 것

FunctionObject가 EnvironmentRecord를 캡쳐해서 [[Environment]]에 저장

 

다음의 동작이 다른 이유

function foo() {

}


const a = foo();
const b = foo();

console.log(a === b) // true

 

Global Execution Context  (Create Phase)
    Lexical Env: (Global)
        foo -> Functional Object(foo)
	Outer: null
    VariableRecord: (Global)


Call Stack (Execution Phase)
a = foo()

Execution Context #1(foo call)
    Lexical Env: (FooEnv #1)
        EnvironmentRecord: {}
        Outer: Global
    ThisisBinding: undifiend
    VariableEnv: (FooEnv #1)

foo returns undefined

Call Stack
b = foo()
    
Execution Context #2
    Lexical Env: (FooEnv #2)
        EnvironmentRecord: undefined
        Outer: Global
    ThisisBinding: undifiend
    VariableEnv: (FooEnv #2)
  
  
foo returns undefined
 
a === b
undefined === undefined → true
function foo() {
    let x = 1;
    return function inner() {
        console.log(x++);
    }
}

const a = foo();
const b = foo();

console.log(a === b); // false

a(); // 1
a(); // 2
b(); // 1

 

Global Execution Context  (Create Phase)
  Lexical Env: (Global)
  	EnvironmentRecord:
    	foo -> Functional Object(foo)
    Outer: null
  Variable Env: (Global)


Call Stack (Execution Phase)
a = foo()

Execution Context #1(foo call)
    Lexical Env: (FooEnv #1)
        EnvironmentRecord:
            x = 1
        Outer: Global
    ThisisBinding: undifiend
    VariableEnv: (FooEnv #1)

inner #1 생성(literal 평가)
inner #1.[[Environment]] = FooEnv #1 클로저 형성

a = inner#1

Call Stack (Execution Phase)
b = foo()

Execution Context #2
    Lexical Env: (FooEnv #2)
        EnvironmentRecord:
            x = 1
        Outer: Global
    ThisisBinding: undifiend
    VariableEnv: (FooEnv #2)
    
inne r#2 생성(literal 평가)
inner #2.[[Environment]] = FooEnv #2 클로저 형성

b = inner #2

a === b
inter#1 === inter#2 ->false

 

 

반환값이 primitive라면 [[Environment]]는 외부에 전달되지 않는다.

클로저가 있을 때만 FunctionObject 내부에 Env가 embed된다.

 

이래서 JS가 인터프리터 언어구나