1. 실행 콘텍스트란?
코드가 실행되기 위해 필요한 환경을 제공하며, 실행 중인 코드의 상태를 나타내는 객체
변수, 함수 선언, 매개 변수 등을 포함한 스코프 정보와 this 값, 외부 환경 정보, 실행 순서 등을 {key: value} 형태로 담고 있다.
실행 콘텍스트는 크게 전역 콘텍스트와 함수 콘텍스트로 나눌 수 있다.
전역 콘텍스트는 스크립트가 로드되면 가장 먼저 생성되는 컨텍스트로 전역 변수와 전역 함수 등을 포함한다.
함수 콘텍스트는 함수가 호출될 때마다 생성되며, 각각의 함수에 대한 지역 변수와 매개 변수, 내부 함수를 포함한다.
var x = 10;
function foo() {
var y = 20;
console.log(x + y);
}
function bar() {
var z = 30;
foo();
console.log(x + z);
}
bar();
1) 전역 콘텍스트 생성
스크립트가 로드되며 전역 콘텍스트가 생성된다. 전역 변수 x가 선언되었고, 함수 foo와 함수 bar가 전역 컨텍스트에 등록된다.
2) 함수 bar 콘텍스트 생성
함수 bar가 호출되면 bar 함수 콘텍스트가 생성된다. 지역 변수 z가 선언되고 함수 foo를 호출한다.
3) 함수 foo 콘텍스트 생성
함수 foo가 호출되면 foo 함수 콘텍스트가 생성된다. 지역 변수 y가 선언되고 console.log(x+y)를 실행해 결과를 출력한다.
4) 함수 foo 콘텍스트 종료
실행이 모두 끝나 종료된다.
5) 함수 bar 콘텍스트
이어서 console.log(x+z)를 실행해 결과를 출력한다.
6) 함수 bar 콘텍스트 종료
실행이 모두 끝나 종료된다.
7) 전역 콘텍스트 종료
스크립트의 실행이 모두 완료되어 종료한다.
위 예시를 보면 실행 콘텍스트는 ✌🏻 스택 형태 ✌🏻 로 관리된다는 것을 알 수 있다.
가장 최근에 추가된 실행 콘텍스트가 먼저 실행되고 제거되는 LIFO 구조다.
2. 실행 콘텍스트의 속성
실행 콘텍스트는 VO, Scope Chain, this Value 3가지 속성으로 구성된다.
1) VO (Variable Object)
현재 콘텍스트에서 사용되는 지역 변수, 매개 변수, 함수 선언 등 정보를 담고 있는 객체
위에서 한 번 설명했는데, 이를 통해 해당 콘텍스트의 변수와 함수를 관리하고, 변수 이름을 키로 사용해 변수 값에 접근할 수 있다. 변수와 함수에 대한 식별자 해결을 위해서 사용된다.
2) Scope Chain
현재 콘텍스트에서 유효한 변수와 함수에 대한 참조를 담고 있는 리스트
전역 컨텍스트부터 현재 컨텍스트까지 스코프 체인을 형성한다.
스코프 체인은 변수나 함수를 찾을 때 사용되며, 변수나 함수를 찾을 때까지 스코프 체인을 따라 올라가 식별자를 해결한다.
실행중인 콘텍스트의 VO에 해당 변수나 함수가 있는지 확인한 후 없으면 상위로 올라가는 그런 과정으로, 전역 콘텍스트까지 올라갔는데 해당 변수나 함수를 찾지 못하면 ReferenceError를 띄운다.
3) this Value
현재 콘텍스트에서의 this 값
this란 함수가 호출될 때 결정되는 특별한 값으로 (함수를 호출한 놈) 함수를 어떻게 호출하느냐에 따라 달라진다.
this Value는 메소드나 생성자 함수에서 주로 사용되며 현재 실행중인 함수를 호출한 객체를 참조한다.
이런 속성들로 실행 콘텍스트는 코드의 변수 범위와 식별자 해결, this 바인딩을 관리한다.
(this 바인딩이란? 위에서 말한 것처럼 함수 호출 방식에 따라 this 값이 되는 대상이 달라진다. 이 때 this 값으로 짝지어지는 대상을 this 바인딩이라고 한다. 아주 개괄적인 이해라 틀린 부분이 있을 수 있다.)
아래 코드를 예시로 이해해보자.
var x = "global";
function outer() {
var x = "outer";
function inner() {
console.log(x);
}
inner();
}
outer(); // "outer"
1) 전역 콘텍스트 생성
변수 x가 선언되어 전역 변수 객체에 추가되고, x는 global로 초기화된다.
2) 함수 outer 콘텍스트 생성
함수 outer가 호출되며 새로운 실행 콘텍스트가 생성되어 스택에 쌓인다.
함수 outer의 변수 객체가 생성되고, x가 선언되어 outer 함수의 변수 객체에 추가된다.
이 때 x는 global에서 outer로 초기화된다.
3) 함수 inner 콘텍스트 생성
함수 inner가 호출되며 새로운 실행 콘텍스트가 생성되어 스택에 쌓인다.
함수 inner의 변수 객체가 생성되고, 변수 객체는 함수 outer의 변수 객체를 참조하는 스코프 체인을 갖는다.
4) 스코프 체인을 따라 올라가기
console.log(x)가 실행되면 현재 실행중인 컨텍스트(함수 inner)에서 x를 찾는다.
하지만 함수 inner의 변수 객체에서는 x에 대한 정보가 없어 상위 콘텍스트로 올라가 x를 찾는다.
이 때 x는 outer로 초기화되어 있으므로 최종적으로 outer가 출력되는 것이다.
3. 렉시컬 환경(Lexical Environment)이란?
클로저를 공부하려고 하니 함수와 함수가 선언된 렉시컬 환경의 조합이라고 하는데, 렉시컬 환경부터 뭔지 몰라 알아봤다.
변수 식별자와 그에 해당하는 값, 상위 스코프에 대한 참조를 저장하는 객체
JS 엔진은 코드 실행 시 현재 실행 콘텍스트의 렉시컬 환경을 구성해 변수 식별자와 값을 연결해준다.
렉시컬 환경은 환경 레코드와 외부 렉시컬 환경 참조라는 2가지 요소로 구성된다.
1) 환경 레코드 (Environment Record)
현재 콘텍스트에서 정의된 변수와 함수 식별자를 저장하는 객체
함수 선언문, 변수 선언문, 매개 변수 등이 여기에 저장되어 스코프에 선언된 정보를 유지하고 접근할 수 있도록 한다.
2) 외부 렉시컬 환경 참조 (Outer Lexical Environment Reference)
현재 콘텍스트와 연결된 상위 스코프의 렉시컬 환경을 참조한다.
이를 통해 변수 식별자의 검색 범위를 확장한다.
스코프 체인이 이를 통해 작동되는 것이다.
렉시컬 환경은 함수 호출 시에만 생성되는 게 아니라 전역 콘텍스트와 모든 블록문에서 생성된다.
이 때 생성된 렉시컬 환경은 실행 콘텍스트의 스택에 저장되며 가장 위에 있는 렉시컬 환경과 대응된다.
이 분의 게시물이 보다 가시적으로 설명을 잘 해주셔서 참고하면 좋다.
4. 클로저 (Closuer)
내부 함수가 외부 함수의 맥락에 접근할 수 있는 것
function outerFunction() {
var outerVariable = 'I am from outer function';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
var closure = outerFunction();
closure(); // 출력: "I am from outer function"
함수 outerFunction은 innerFunction을 정의하고 반환한다.
반환된 innerFunction는 외부 변수 outerVariable에 접근하고, 출력한다.
outerFunction은 호출된 후에도 outerVariable에 접근할 수 있는 클로저를 형성한다.
코딩 테스트를 풀어 보거나 개발을 해보면서 이해할 수 있는 개념이지만
어떤 단어로 명시하고 특징을 줄줄 나열하려니 다소 어렵게 느껴진다.
그냥 간단하게 함수가 선언된 환경의 스코프를 기억해서 함수가 스코프 밖에서 실행될 때도 이 스코프에 접근할 수 있게 하는 것이라고 이해했다.
마찬가지로 이 분의 설명이 좋아 참조하면 이해가 더 잘 될 것 같다.
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] 객체 Set을 알아보자 (0) | 2023.06.12 |
---|---|
[JS] 객체 Map을 알아보자 (0) | 2023.06.09 |
[JS] var, let, const 변수에 대해 알아보자 (feat. 자바스크립트 ES6+) (0) | 2023.05.26 |
[JavaScript] Generator & Iterator (0) | 2023.05.12 |
[JavaScript] 정규표현식 알아보기 (0) | 2023.04.28 |