JavaScript 스코프와 클로저

JavaScript 스코프

JavaScript에서 스코프란 변수가 어느 지역에서 유효한 것을 의미하는 중요한 개념이다. JavaScript는 함수를 기반으로 설계 되었기 때문에 기본적으로 함수 레벨의 스코프를 따르게 되는데 이는 C++, JAVA의 변수 유효범위와는 차이를 보인다.

1
2
3
4
5
6
7
8
9
void getArea(bool condition,int x,int y)
{
if(condition)
{
int temp = 3;
}

return temp * x * y;
}
1
2
3
4
5
6
7
8
9
function getArea(condition, x, y)
{
if(condition)
{
var temp =3;
}

return temp * x * y;
}

첫번째는 C, 두번째는 JavaScript에서의 경우의 함수이다. 위의 결과를 실행하게 된다면 C에서는 당연히 컴파일 에러가 날 것이다. 하지만 Javascript는 문제없이 실행 되고 3 x y라는 결과를 반환한다.

C, Java에서는 블록에서 선언한 변수가 블록 내에서 유효한 반면 JavaScript는 함수 레벨 스코프를 가지기 때문에 if 안에서 선언한 temp 변수도 getArea라는 함수에 속하는 변수로 판단된다.

이와 같은 성질은 평소에 C나 JAVA를 해왔던 프로그래머들에게 익숙하지 않은 개념이고 ECMA6부터는 let과 const라는 블록레벨 스코프를 지원한다. let은 우리가 늘 쓰던 지역변수이고 const는 블록레벨 상수 변수이다.

실행 컨텍스트

실행 컨텍스트는 실행가능한 코드가 실행되는 환경이다. 자바스크립트 엔진은 코드를 실행하기 위해 실행에 필요한 여러가지 정보를 알고 있어야 한다. 실행에 필요한 정보는 아래와 같은 것들이 있다.

  • 변수 : 전역 변수, 지역 변수, 매개변수, 객체의 프로퍼티
  • 함수 선언
  • 변수의 유효 범위
  • this

실행 컨텍스트는 객체와 같이 만들어져서 실행 도중 새로운 컨텍스트(함수)를 만나면 스택을 쌓아 새로운 컨텍스트를 호출한다. 이와 같이 실행 컨텍스트는 스택에 쌓여 호출 되면 쌓이고 실행이 완료되면 pop되는 구조를 가지고 있다.

실행 컨텍스트에는 객체화 하여 3가지의 프로퍼티를 소유한다.

  • VO (변수 , 함수선언, 인자 등)
  • Scope Chain (변수 오브젝트 + 모든 부모의 스코프)
  • this (함수의 호출에 의해 결정되는 this)

스코프 체인

ScopeChain

스코프 체인은 일종의 리스트로 함수의 스코프 레퍼런스를 차례로 저장하고 있는 개념이다. 즉 스코프 체인은 현재 실행 컨텍스트의 AO를 선두로 상위 컨텍스트의 AO를 모두 가르키는 레퍼런스 테이블을 만든다.

실행 컨텍스트가 전역일 경우 이는 VO는 유일한 GO(Global Ovject)를 가르키게 된다. 또한 실행 컨텍스트 중 foo()라는 함수가 만나면 스택에 실행 컨텍스트가 쌓이고 foo()의 스코프에 해당하는 변수, 함수선언들에 대한 AO를 만들게 된다. foo()의 스코프 체인은 0번째는 자신의 AO를 가르키고 그다음은 자신의 상위 부모를 가르키게 된다.

(만약 부모가 여러명이라면 모든 부모를 가르킬 때 까지 스코프 체인을 이어나간다.)

클로저

클로저는 내부함수가 참조하는 외부함수의 지역변수가 외부함수에 의해 내부함수가 반환된 이후에도 life-cycle이 유지되는 것을 의미한다.

1
2
3
4
5
6
7
8
9
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
return innerFunc;
}

// 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다. 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다
var inner = outerFunc();
inner();

위의 코드에서 outerFunc()을 호출하여 innerFunc을 받아 전역 변수 inner에 저장하고 inner를 실행한다. 그렇다면 cosole.log(x)에 x가 찍힐까?

-> 그렇다

자바 스크립트는 함수 레벨 스코프를 가진다면서 왜 x의 값은 사라지지 않고 내부함수가 이를 참조하여 출력할 수 있을까? 이를 이해하려면 위에서의 실행컨텍스트를 이해하고 있어야 한다.

위의 실행 컨텍스트에서 보면 이를 이해할 수 있다. outerFunc()은 실행 후 실행 컨텍스트에서 종료되어 그 실행 컨텍스트는 없어진다. 하지만 inner function의 스코프 체인이 outerFunc()의 AO를 가르키고 있어 AO 내부의 변수 x에 참조가 가능한 것이다.

-> 다시 말하면 실행 컨텍스트는 사라져도 AO는 바로 사라지지 않는다. 자신을 참조하고 있는 개체가 하나이상이라면 AO는 가비지 콜렉터에 의해 소멸되지 않는다.

공유하기