실행 컨텍스트, 클로저 / JavaScript
28413 단어 JavaScriptJavaScript
실행 컨텍스트(Execution context)
실행 컨텍스트의 개념
- C 언어에서 함수가 호출될 때마다 해당 함수의 호출 정보가 기존 함수의 호출 정보 위에 스택형태로 하나씩 쌓인다. -> 콜 스택(Call Stack) / 따라서 개발자는 콜 스택의 호출 정보 등으로 코드의 실행 과정을 추적하여 디버깅과 같은 작업을 수행한다.
- 실행 컨텍스트는 콜 스택에 들어가는 실행 정보 하나와 비슷하다.
- 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념 -> 실행가능한 자바스크립트 코드 블록(함수)이 실행되는 환경
- 현재 실행되는 컨텍스트에서 이 컨텍스트와 관련 없는 실행 코드가 실행되면, 새로운 컨텍스트가 생성되어 스택에 들어가고 제어권이 그 컨텍스트로 이동한다.
- 전역 코드, eval() 함수로 실행되는 코드, 함수 안의 코드를 실행할 경우 실행 컨텍스트가 형성된다.
<script>
console.log("This is global context");
function ExContext1() {
console.log("This is ExContext1");
};
function ExContext2() {
ExContext1();
console.log("This is ExContext2");
};
ExContext2();
</script>
-- 실행 컨텍스트 스택이 전역 컨텍스트 -> ExContext2 -> ExContext1 순으로 쌓이게 된다.
실행 컨텍스트 생성 과정
활성 객체 생성
- 실행 컨텍스트가 생성되면 자바스크립트 엔진은 해당 컨텍스트에서 실행에 필요한 여러 가지 정보를 담을 객체를 생성하는데, 이를 활성 객체라고 한다.
- 활성 객체에 사용하게 될 매개변수나 사용자가 정의한 변수 및 객체를 저장하고, 새로 만들어진 컨텍스트로 접근 가능하게 된다.(사용자의 접근 X, 엔진 내부에서 접근 O)
arguments 객체 생성
- 다음 단계에서 arguments 객체를 생성한다. 앞서 만들어진 활성 객체는 arguments 프로퍼티로 arguments 객체를 참조한다.
스코프 정보 생성
- 다음 단계에서 현재 컨텍스트의 유효 범위를 나타내는 스코프 정보를 생성한다.
- 스코프 정보는 현재 실행 중인 실행 컨텍스트 안에서 연결 리스트와 유사한 형식으로 만들어진다. 현재 컨텍스트에서 특정 변수에 접근해야 할 경우, 이 리스트를 활용한다. 이 리스트로 현재 컨텍스트의 변수뿐 아니라, 상위 실행 컨텍스트의 변수도 접근이 가능하다.
- 이 리스트를 스코프 체인이라 하고 [[scope]] 프로퍼티로 참조된다. 현재 생성된 활성 객체가 스코프 체인의 제일 앞에 추가된다.
변수 생성
- 다음 단계에서 현재 실행 컨텍스트 내부에서 사용되는 지역 변수의 생성이 이루어진다. 생성되는 변수를 저장하는 변수 객체는 활성 객체가 변수 객체로 사용되는 것으로 둗 객체는 같은 객체이다.
- 변수 객체 안에서 호출된 함수 인자는 각각의 프로퍼티가 만들어지고 그 값이 할당된다. 만약 값이 넘겨지지 않았다면 undefined가 할당된다. 그리고 함수 안에 정의된 변수, 함수가 생성된다. 이 과정에서는 변수나 내부 함수를 단지 메모리에 생성하고, 초기화는 각 변수나 함수에 해당하는 표현식이 실행되기 전까지는 이루어지지 않는다는 점이다. 따라서 변수에 먼저 undefined가 할당된다.
this 바인딩
- 마지막 단계에서 this 키워드를 사용하는 값이 할당된다. 여기서 this가 참조하는 객체가 없으면 전역 객체를 참조한다.
코드 실행
스코프 체인(Scope chain)
- 자바스크립트에서는 오직 함수만이 유효 범위의 한 단위가 된다. 이 유효 범위를 나타내는 스코프가 [[scope]] 프로퍼티로 각 함수 객체 내에서 연결 리스트 형식으로 관리되는데, 이를 스코프 체인이라고 한다.
- 스코프 체인은 실행 컨텍스트의 변수 객체(활성 객체)가 구성 요소인 리스트와 같다. -> 스코프 체인[(3, ...), (2, 변수 객체 2), (1, 변수 객체 1), (0, 변수 객체 0)]
- 각각의 함수는 [[scope]] 프로퍼티로 자신이 생성된 실행 컨텍스트의 스코프 체인을 참조한다. 함수가 실행되는 순간 실행 컨텍스트가 만들어지고, 이 실행 컨텍스트는 실행된 함수의 [[scope]] 프로퍼티를 기반으로 새로운 스코프 체인을 만든다.
전역 실행 컨텍스트의 스코프 체인
<script>
var var1 = 1;
var var2 = 2;
console.log(var1); // (출력값) 1
console.log(var2); // (출력값) 2
</script>
1) 위 코드는 전역 코드이다. 함수가 선언되지 않아 함수 호출이 없고, 실행 가능한 코드들만 나열되어 있다.
2) 위 코드가 실행되면, 먼저 전역 실행 컨텍스트가 생성되고, 변수 객체가 만들어진다.
3) 현재 전역 실행 컨텍스트 단 하나만 실행되고 있어 참조할 상위 컨텍스트가 없다. 자신이 최상위에 위치하는 변수 객체이다.
4) 따라서, 이 변수 객체의 소코프 체인은 자기 자신만을 가진다.
5) 변수 객체의 [[scope]]는 변수 객체 자신을 가리킨다.
함수 호출시 생성되는 실행 컨텍스트의 스코프 체인
<script>
var var1 = 1;
var var2 = 2;
function func() {
var var1 = 10;
var var2 = 20;
console.log(var1); // (출력값) 10
console.log(var2); // (출력값) 20
}
func();
console.log(var1); // (출력값) 1
console.log(var2); // (출력값) 2
</script>
1) 코드 실행시 전역 실행 컨텍스트가 생성되고, func() 함수 객체가 만들어진다.
2) 함수 객체가 생성될 때, 그 함수 객체의 [[scope]]는 현재 실행되는 컨텍스트의 변수 객체에 있는 [[scope]]를 그대로 가진다. 따라서, func 함수 객체의 [[scope]]는 전역 변수 객체가 된다.
3) 스코프 체인[(1, func 변수 객체), (0, 전역 객체)]
- 각 함수 객체는 [[scope]] 프로퍼티로 현재 컨텍스트의 스코프 체인을 참조한다.
- 한 함수가 실행되면 새로운 실행 컨텍스트가 만들어지는데, 이 새로운 실행 컨텍스트는 자신이 사용할 '스코프 체인'을 현재 실행되는 함수 객체의 [[scope]] 프로퍼티를 복사하고, 새롭게 생성된 변수 객체를 해당 체인의 제일 앞에 추가하는 방법으로 만든다.
- 스코프 체인 = 현재 실행 컨텍스트의 변수 객체 + 상위 컨텍스트의 스코프 체인
- 스코프 체인으로 식별자 인식이 이루어진다. 스코프 체인의 첫 번째 변수 객체부터 시작하며 식별자와 대응되는 이름을 가진 프로퍼티가 있는지를 확인한다. 변수 객체의 공식 인자, 내부 함수, 지역 변수를 확인하고 다음 객체로 이동하여 찾는다.
클로저(Closure)
클로저의 개념
- 이미 생명주기가 끝난 외부 함수의 변수를 참조하는 함수를 클로저라고 한다.
<script>
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); }
return innerFunc;
}
var inner = outerFunc();
inner(); // (출력값) 10
</script>
1) outerFunc에서 선언된 x를 참조하는 innerFunc가 클로저
2) 클로저로 참조되는 외부 변수 즉, outerFunc의 x와 같은 변수를 '자유 변수'라고 한다. 클로저는 자유 변수에 엮여 있는 함수이다.
<script>
function outerFunc(arg1, arg2) {
var local = 8;
function innerFunc(innerArg) {
console.log((arg1 + arg2) / (innerArg + local))
}
return innerFunc;
}
var exam1 = outerFunc(2, 4);
exam1(2);
</script>
클로저 활용
<script>
var getCompletedStr = (function () {
var buffAr = [
'I am ',
'',
'. I live in ',
'',
'. I am ',
'',
' years old.',
];
return (function (name, city, age) {
buffAr[1] = name;
buffAr[3] = city;
buffAr[5] = age;
return buffAr.join('');
});
})();
var str = getCompletedStr('zzoon','seoul', 16);
console.log(str);
</script>
--> I am zzoon. I live in seoul. I am 16 years old.
<script>
function callLater(obj, a, b) {
return (function () {
obj["sum"] = a + b;
console.log(obj["sum"]);
});
}
var sumObj = {
sum : 0
}
var func = callLater(sumObj, 1, 2);
setTimeout(func, 2000);
</script>
--> 2초 뒤, 3
<script>
function outerFunc(argNum) {
var num = argNum;
return function (x) {
num += x;
console.log('num: ' + num);
}
}
var exam = outerFunc(40);
exam(5);
exam(-10);
</script>
--> num : 45, num : 35
- 클로저의 프로퍼티값이 쓰기 가능하므로 그 값이 여러 번 호출로 항상 변경할 수 있음
<script>
function func() {
var x = 1;
return {
func1 : function () { console.log(++x); },
func2 : function () { console.log(-x); }
};
};
var exam = func();
exam.func1();
exam.func2();
</script>
--> 2, -2
- 하나의 클로저가 여러 함수 객체의 스코프 체인에 들어가 있는 경우
<script>
function countSeconds(howMany) {
for (var i = 1; i <= howMany; i++) {
(function (currentI) {
setTimeout(function () {
console.log(currentI);
}, currentI * 1000);
})(i);
}
};
countSeconds(3);
</script>
--> 1, 2, 3
Author And Source
이 문제에 관하여(실행 컨텍스트, 클로저 / JavaScript), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@aljongjong/실행-컨텍스트-클로저-JavaScript저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)