minzzl

[Javascript] Scope 와 Closure 본문

프로젝트/자바스크립트

[Javascript] Scope 와 Closure

minzzl 2022. 10. 9. 21:39
728x90
반응형

Scope

 

우선 아래의 코드부터 보겠습니다.

x와 y는 자신이 선언된 위치에 따라 본인의 유효범위가 결정되었습니다. 즉 변수 x, y에 대해 다른 코드가 참조할 수 있는 어떠한 범위가 결정 될 것 인데요, 이러한 유효범위를 scope라고 합니다. 

 

scope 정의

변수 이름, 함수 이름, 클래스 이름과 같은 식별자가 본인이 선언된 위치에 따라 다른 코드에서 자신이 참조 될 수 있을지 없을지 결정되는 것

 

그렇다면 만약 변수가 전역에 선언되어있지만 지역에는 없고, 지역에도 선언되어있고 전역에도 선언되어 있다면 어떻게 실행이될까요?

scope 체인

 

위의 그림과 같이 함수는 중첩이 될 수 있습니다. 각 함수들의 중첩이 일어난다면 scope도 중첩이 일어납니다. 이는 scope가 함수의 중첩에 의해 계층적인 구조를 가질 수 있다는 것을 의미합니다. 

 

아래의 코드를 살펴봅시다.

코드가 이해가 되시나요? 이를 시각화 해서 나타내보겠습니다.

 

오른 쪽과 같이 스코프가 계층적으로 연결되어 있는 것을 scope 체인이라고 하며 자바스크립트 엔진은 변수를 참조할 때 이를 참조합니다.

만약 변수를 참조하려고 할 때 현재 스코프에 해당 변수가 없다면 상위 스코프로 올라갑니다. 그렇게 쭉쭉 올라가다보면 전역 스코프로 올라가게 될 텐데 여기에도 없다면 refernce err 가 나옵니다.

 

이러한 scope는 어떤 level을 가지느냐에 따라 다음과 같이 나눌 수 있습니다.

대부분의 프로그래밍 언어는 블록 레벨 스코프를 가지지만 자바스크립트는 함수 레벨의 스코프를 가집니다. 특히 var 키워드로 선언된 변수의 경우 오로지 함수 코드 블록 만을 지역 스코프로 인정합니다. 그런데 다른 언어에도 있는 블록 레벨 스코프는 let 과 const 키워드를 사용하면 블록 레벨 스코프가 가능합니다.

 

 

또한 상위 스코프가 결정되는 시점에 따라 동적 스코프와 정적 스코프로 나뉩니다.

자바스크립트는 렉시컬 스코프를 따르기 때문에 함수는 태어나자마지자 상위 스코프가 결정이 되고, 함수 객체가 생성이 되면 해당 함수 객체는 본인의 상위 스코프를 항상 알 수 있게됩니다. 이렇게 해당 함수가 상위 스코프를 항상 할 수 있는 이유는 자바스크립에서 함수는 태어나면서 자신의 내부 슬롯에 상위 스코프의 참조를 저장하기 때문입니다.

 

 

함수 호출 시의 흐름은 다음과 같습니다.

이 때 렉시컬 환경이란 어떠한 코드가 어디서 실행되고 본인 주변에 어떤 코드들이 있는지 대체적인 정보를 담고 있는 환경입니다. 즉 함수 본인 내부의 식별자, 식별자의 바인딩 된 값 등을 기록하고 있는 하나의 자료구조입니다.

 

실행이 끝나면 실행 컨텍스트 스택에서 해당 컨텍스트를 pop하여 제거합니다.

 

그렇다면 아래의 코드도 한번 살펴보겠습니다. 

 

 

 

함수 호출 시 일어나는 일을 살펴봅시다.

outer()가 호출되면 실행 컨텍스트에 들어가게 됩니다. outer 함수는 중첩함수 inner를 엘라에게 반환하면서 생명주기를 마감합니다. 즉 outer 함수의 호출이 종료되면 실행 컨텍스트 스택에서 제거가 되고 변수 x또한 생명주기를 마감합니다.

 

그러나 결과 값은 생명주기를 마감함 x 값이 출력됩니다.

이때 사용된 개념이 closure입니다.

Closure

방금 전 예시를 통해 말해보자면, 중첩함수 inner가 이미 생명주기를 마감함 outer 함수의 지역 변수 x를 참조할 수 있다면 이 때의 inner를 클로저라고합니다.

 

outer 함수 종료전의 렉시컬 환경의 모습입니다.

outer 함수가 종료 될 경우 실행 컨텍스트에서 제거됩니다. 그리고 outer 함수는 inner 함수를 반환하면서 사라지기 때문에 ella는 inner 함수객체를 참조합니다.

outer 함수는 컨텍스트 스택에서 완전히 제거가 되었지만 outer의 렉시컬 환경 자체는 소멸되지 않았습니다. 이는 ella가 inner 함수를, inner 함수는 outer 환경의 렉시컬 환경을 참조하기 때문입니다. 따라서 inner 함수는 상위 스코프에 의존하여 상위 스코프의 식별자를 참조할 수 있는 것입니다. 

 

즉 closure란 중첩함수가 상위 스코프의 식별자를 참조하고 있고 본인의 외부 함수보다 더 오래 살아있다면 이 중첩함수는 클로져입니다.

 

이러한 클로져는 본인의 상위 스코프에서 현재 참조하는 식별자만을 기억하고 있는데요, 이러한 클로져는 하나의 state가 의도치 않게 변경되지 않도록 은닉하고 특정 함수에서만 state 변경을 허용하기 위해서 사용합니다.

 

 

* 아래의 영상을 정리하여 작성하였습니다.

https://www.youtube.com/watch?v=PVYjfrgZhtU

 

 

 

 

 

728x90
반응형