melius
[JS] 실행문맥과 클로저 본문
1. 실행문맥(Execution Context)
실행문맥(Execution Context)은 함수와 같은 코드블록이 실행되는 환경이다.
함수호출시에 실행문맥이 생성되어 콜 스택에 쌓인다.
실행문맥이 생성되면,
1) 변수객체(Variable Object) 생성
코드블록이 사용할 변수를 담는 변수객체(Variable Object)를 생성한다.
변수객체에는 함수의 인수를 담는 변수인 arguments와 코드블록 내에 선언된 지역변수가 생성된다.
전역실행문맥의 변수객체인 전역객체(Global Object)에는 함수과 같이 인수가 없으므로 arguments 객체가 없다.
함수실행문맥의 변수객체를 활성객체(Activation Object)라 한다.
2) 스코프 체인(Scope Chain) 생성
변수의 유효범위를 나타내는 스코프 체인이 생성된다.
콜 스택에 쌓인 각 실행문맥의 변수객체가 체인의 구성요소가 되며, 체인의 마지막은 전역객체이다.
스코프 체인을 통해서 콜 스택에 쌓인 다른 실행문맥의 변수에 접근이 가능하다.
* 객체의 프로퍼티는 프로토타입 체인으로 접근
3) this 바인딩 및 코드블록 실행
this 키워드가 특정객체에 바인딩 되고, 코드블록이 실행된다.
전역실행문맥에서는 this가 전역객체에 바인딩된다.
2. 클로저(Closure)
함수가 종료되어도 함수 실행시 생성된 실행문맥(EC1)의 변수객체(VO1)를 참조하는 외부변수가 있으면, 해당 변수객체(VO1)는 실행 중 실행문맥(EC2)인 스코프 체인(SC2) 안에 살아있다.
종료된 실행문맥(EC1)의 변수객체(VO1)에 접근하는 함수를 클로저(Closure)라고 하고 주로 내부함수이다.
참조되는 변수객체(VO1)의 지역변수를 자유변수(Free Variable)이라고 한다.
클로저 작동 메커니즘
함수가 선언되면, 선언시의 실행문맥(EC1)의 스코프 체인(SC1)이 선언된 함수의 [[Scope]] 속성에 저장한다.
즉, 전역실행환경에서 선언된 함수의 [[Scope]] 속성에는 전역객체만 있고, 내부함수의 [[Scope]] 속성에는 외부함수호출시에 생성된 실행문맥(EC1)의 스코프 체인(SC1)이 있다.
함수가 호출되면, 호출시에 생성된 실행문맥(EC2)의 스코프 체인(SC2)은 호출된 함수의 [[Scope]] 속성에 저장해둔 스코프 체인(SC1)에 함수가 호출되며 생성되는 변수객체(VO2)를 연결한 체인이 된다. (SC2 = SC1 + VO2)
* 크롬 콘솔에서 함수의 [[Scopes]] 속성으로 확인 가능하다.
let x = 1;
function outerFun1() {
function innerFun1() { return x; }
return innerFun1;
}
let fun1Var = outerFun1();
console.log(fun1Var()); // 1
function outerFun2() {
let x = 2; // free variable
function innerFun2() { // closure
return x;
}
return innerFun2;
}
let fun2Var = outerFun2();
console.log(fun2Var()); // 2
console.dir(fun2Var);
// [[Scopes]]: Scopes[3]
// 0: Closure (outerFun2) {x: 2}
// 1: Script {…}
// 2: Global {…}
var 선언문과 let 선언문
아래 코드와 같이 DOM 요소의 이벤트 핸들러를 for 문을 이용하여 작성시, 카운터 변수를 var 문으로 선언한 경우와 let 문으로 선언한 경우가 다르게 동작한다.
<button>btn0</button>
<button>btn1</button>
<button>btn2</button>
<script>
var btnList = document.getElementsByTagName('button');
for (var i = 0; i < btnList.length; i++) {
btnList[i].onmouseover = function () {
console.log(i); // 3
}
}
console.log(i); // 3
for (let j = 0; j < btnList.length; j++) {
btnList[j].onclick = function () { // closure
console.log(j); // free variable
}
}
</script>
var 문으로 선언된 변수 i는 Function Scope를 가지므로 for 문이 끝나도 변수에 접근이 가능하고, 각 이벤트 핸들러에서 접근하는 i도 모두 같은 변수이다. 반면에, let 문으로 선언된 변수 j는 Block Scope를 가지므로, 매 반복시에 다른 지연변수 j가 생성되므로 각 이벤트 핸들러(클로저)에서 접근하는 j는 모두 다른 변수(자유변수)이다.
'JavaScript' 카테고리의 다른 글
[JS] Promise (0) | 2020.02.19 |
---|---|
[JS] Array, Set, Map (0) | 2020.01.08 |
[JS] 문장과 표현식 (0) | 2020.01.03 |
[JS] 프로토타입 (0) | 2020.01.01 |
[JS] this (0) | 2020.01.01 |