본문 바로가기

Computer Programming/CS

자바스크립트의 클로저 동작 방식 (렉시컬환경과 렉시컬 스코프)

1. 렉시컬 환경

 

어떤 함수 A에서 선언한 변수 a를 참조하는 내부 함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상을 클로저라고 합니다.

 

클로저는 LE와 연관이 있기 때문에 이를 먼저 간단하게 설명하겠습니다.

 

LE는 Lexical Environment로, 실행할 스코프 범위 안에 있는 변수와 함수를 프로퍼티로 저장하는 객체입니다.

두 가지 구조로 나누어져 있으며 하나는 자신의 지역 변수를 프로퍼티로 저장하고 있는 객체인 '환경레코드(Record)' 이고 하나는 상위의 렉시컬 환경인 외부 렉시컬환경(Outer) 입니다.

 

'LE' = {
    Environment Record: {
  		변수객체: [Object],
        scopeChain: [Object],
    },
    Outer Environment Reference,
    this: [Object]
}

 

 

이제 아래의 코드를 살펴보겠습니다.

function sum(x) {
    return function(y) {
    	return x + y;
    }
}

const add = sum(2);
console.log(add(7))

이 코드는 sum 함수는 익명함수를 리턴하고, 이 익명 함수를 add에 할당해 add 변수를 호출한 결과를 출력하는 코드입니다.

 

 

동작하는 과정을 보면

 

1. 코드 평가를 진행하며 Global LE를 생성합니다. 이 때 sum 함수와 add 변수를 호이스팅해 Record 에 기록하고 Outer은 null이 할당되어 콜스택에 가장 먼저 push 됩니다.

 

 

2. 코드가 순차적으로 실행되면서 add 변수에 값을 할당하기 위해 sum함수를 호출합니다. 이어 sum의 LE를 생성하고  Record 에는 파라미터 x와 return문 안의 익명함수가 호이스팅 되어 기록됩니다. Outer는 Global LE를 가리킵니다.

3. x 변수가 2로 할당되고 sum은 종료되어 pop()됩니다. 

4. Global Record의 add에 sum의 리턴 값인 익명함수를 할당합니다.

5. 마지막으로 console.log를 실행하기 위해 add 함수를 호출하고

 

const add = function(y){
    return x + y
}
console.log(add(7))

 

이 익명함수를 실행하기 위해 내부 코드 평가가 이루어지는데, Outer에는 x 변수를 참조하기 위해 상위 스코프인 sum을 참조해야 합니다. 그러나 이미 종료되어 pop됬기 때문에 참조 할 수 없는 것 처럼 보입니다.

 

 

그러나 자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라, 함수를 어디에 정의했는지에 따라 상위 스코프를 결정합니다. 이를 렉시컬 스코프라고 합니다.

 


2. 랙시컬 스코프와 클로저

 

function sum(x) {
    return function(y) {
    	return x + y;
    }
}

const add = sum(2);
console.log(add(7))

 

이 코드를 다시 보면 익명함수는 sum함수 안에서 정의되고 있습니다. 따라서 자신의 상위 스코프는 global이 아닌 sum이 되고, Outer의  sum 을 참조하여 콜스택에 push 됩니다.

 

 

sum의 LE의 Record에 x=2; 가 할당 되어 있겠죠? 이 변수를 참조하면 됩니다. 이 x 변수와 같은 것을 자유변수라고도 부릅니다.

 

 

 

 

즉 sum의 LE는 콜스택에 존재하지는 않지만 (실행이 종료되서) add의 LE는 자신이 정의된 곳을 기준으로 상위 스코프를 결정하기 때문에 여전히 Outer에서 sum을 참조할 수 있습니다. 호출되는 순서에 상관없이 이러한 변수가 사라지지 않는 현상을 클로저 현상이라고 말할 수 있습니다.

 

 

 

 

https://webcoding-start.tistory.com/67