⚡실행컨텍스트 (execution context) 란?
실행컨텍스트는 자바스크립트의 동작 원리를 담고있는 핵심 개념이다.
실행컨텍스트를 바르게 이해하면 자바스크립트가 스코프를 기반으로 식별자와 식별자에 바인딩 된 값(식별자 바인딩) 을 관리하는 방식과 호이스팅이 발생하는 이유, 클로저의 동작 방식
그리고 태스크 큐와 함께 동작하는 이벤트 핸들러와 비동기 처리의 동작 방식을 이해할 수 있다.
-자바스크립트 모던 딥다이브 359p
본 글은 모던 자바스크립트 딥다이브 내용을 참고하여 작성하지만 제가 이해한 바를 같이 서술하다보니
틀린 점이 있을 수도 있습니다. 최대한 맞게 이해해보려고 노력하겠지만 틀린점이 있으면 지적해주세요
전혀 와닿지는 않는 설명입니다만 아무튼 핵심이고 중요하다는 건 전해지네요
이걸 이해하기 위해선 먼저 ECMAScript의 소스코드 분류 방식을 알 필요가 있습니다.
소스코드는 총 네가지로 이루어져있고 이 4가지 타입의 소스코드는 실행 컨텍스트를 생성합니다.
이 소스코드는 다음과 같습니다.
소스코드의 타입
전역코드 | 전역에 존재하는 소스코드 전역에 정의 된 함수,클래스 등의 내부 코드는 포함 x 전역 코드는 전역 변수의 관리를 위해 최상위 스코프(전역 스코프)를 생성해야함. var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 함. -> 전역 코드가 평가되면 전역 실행 컨텍스트가 생성 |
함수코드 | 함수 내부에 존재하는 소스코드 근데 함수 내부에 중첩된 함수, 클래스, 내부 코드는 포함 x 함수코드는 지역 스코프를 생성하고 지역변수, 매개변수, arguments 객체를 관리해야함 생성한 지역 스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결해야함 -> 함수코드가 평가되면 함수 실행 컨텍스트 생성 |
eval코드 | 빌트인 전역 함수 eval함수에 인수로 전달되어 실행되는 코드 eval 코드는 strict mode에서 자신만의 독자적인 스코프를 생성함 -> eval 코드가 평가되면 eval 실행 컨텍스트 생성 |
모듈코드 | 모듈 내부에 존재하는 소스코드 모듈 내부의 함수,클래스 등의 내부 코드는 포함 x 모듈 코드는 모듈 별로 독립적인 모듈스코프를 생성함 -> 모듈 코드가 평가되면 모듈 실행 컨텍스트 생성 |
근데 이거 굳이 구분해야함?
구분해야하는 이유는 소스코드의 타입에 따라
실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문입니다.
사실 저 위에 표에 다 적어놓은 내용인데 안읽으셨죠?
사실 표에 안 적어놓은 내용임 ㅎㅎ;;
🔍소스코드의 평가와 실행
앞서 우리가 알 수 있었던걸 요약하면
실행 컨텍스트는 모종의 이유로 인해 총 4가지로 나뉘어서 생성을 하는데 이 생성은
소스코드 평가 -> 소스코드의 실행
이라는 2가지 과정으로 이루어진다는 것이었습니다.
근데 뭘 평가하고 뭘 실행하는 건가요
소스코드의 평가 | 선언문을 평가합니다. 선언문은 let,var,const,함수선언문,class등이 있습니다. 이때 평가 과정에서 변수는 실행컨텍스트가 관리하는 스코프에 등록되고 값은 undefined로 초기화됩니다. |
소스코드의 실행 | 선언문을 제외한 소스코드가 순차적으로 실행 (런타임) 이때 소스코드 실행에 필요한 정보(변수, 함수의 참조)를 실행 컨텍스트가 관리하는 스코프에서 검색하여 취득한다. 그리고 변수 값의 변경 등 소스코드의 실행 결과는 다시 실행컨텍스트가 관리하는 스코프에 등록 |
ㄹㅇ 이뭔씹의 극치지만 예제를 통해 보면 좀 나을 것 같네요
var stupid
//소스코드 평가 과정에서 처리 이때 stupid의 값은 undefined
stupid = "you"
//stupid에 "you"를 할당 이 코드의 실행 시점은
//소스코드 실행 과정 stupid의 값은 이때 비로소 "you"가 됨
이때 var stupid는 소스코드 평가 단계에서 이미 실행이 완료되었습니다.
왜?-> var stupid는 선언문이니까 평가단계에서 실행됨
그리고 평가단계가 끝난 뒤
stupid = "you"라는 변수 할당문이 소스코드 실행 단계에서 실행됩니다.
🔞근데 주의할점
평가단계가 끝난 뒤 할당문들이 실행이 되는데 변수에 값을 할당하려면
먼저 변수가 선언된 변수인지 확인해야 합니다.
위 예제에서는 stupid 변수가 실행 컨텍스트가 관리하는 스코프에 등록되어 있는지를 확인해야합니다.
만약 선언된 변수라면 값을 할당해주고 할당한 결과를 실행 컨텍스트에 등록하여 관리합니다.
그니까 쉽게 생각하면 소스 평가 -> 실행 두가지 단계를 거쳐서 실행컨텍스트를 만드는데
소스 평가 단계에서 변수는 실행컨텍스트가 관리하는 스코프에 등록되면서 값은 undefined로 초기화되고
소스 실행 단계에서 내가 설정한 값을 할당받는다고 볼 수 있는 거고
이 실행컨텍스트는 전역, 함수 , eval , 모듈 네 종류가 있다는거군요!
🔍실행 컨텍스트의 역할
//전역 변수 선언
const x = 1
const y = 2
//함수 정의
function foo (a) {
//지역변수 선언
const x = 10
const y = 20
//메서드호출
console.log(a + x + y) // 130
}
//함수 호출
foo(100)
//메서드 호출
console.log(x + y) // 3
위 예제를 자바스크립트 엔진이 어떻게 평가하고 실행할지를 생각해봅시다.
순서에 따라서 봐보자구요
1. 전역코드 평가
전역코드를 실행하려면 먼저 전역 코드를 평가해야 합니다. 그것이... 우리가 지금까지 본 과정이니까..
이때 선언문만 먼저 실행을 해줍니다. 따라서 이때 실행되는 대다수의 코드는 다음과 같을 것입니다.
변수 선언문 , 함수 선언문이 실행 될 것이고 그 결과 생성된
전역 변수와 전역 함수가 실행 컨텍스트가 관리하는 전역 스코프에 등록됩니다.
이때 var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 됩니다.
2. 전역코드 실행
전역 코드 평가 과정이 끝나면 런타임이 시작됩니다.
전역 코드가 순차적으로 실행되기 시작하며 전역 변수에 값이 할당되고 함수가 호출됩니다.
💥**함수가 호출되면 순차적으로 실행되던 전역 코드의 실행을 일시중단하고💥
코드 실행 순서를 변경하여 함수 내부로 진입합니다.
3. 함수 코드 평가
2번 과정에서 함수 호출에 의해 코드 실행 순서가 변경되어서 함수 내부로 진입하면
함수 내부의 문들을 실행하기에 앞서 함수 코드 평가 과정을 거칩니다.
매개변수와 지역 변수 선언문이 먼저 실행되고
그 결과로 생성된 매개변수와 지역 변수가 실행 컨텍스트가 관리하는 "지역" 스코프에 등록되고
함수내부에서 지역변수처럼 사용할 수 있는 arguments 객체도 생성되어 지역 스코프에 등록되며
this 바인딩도 이때 결정됩니다.
4. 함수 코드 평가
함수 코드의 평가 과정이 끝나면 런타임이 시작되어 순차적으로 함수코드가 실행됩니다.
이때 매개 변수와 지역 변수에 값이 할당되고 console.log()메서드가 호출됩니다.
☢이때 console.log() 메서드를 호출하기 위해선?
1. 먼저 식별자인 console을 스코프 체인을 통해 검색합니다.
이를 위해 함수 코드의 지역 스코프는 상위 스코프인 전역 스코프와 연결되어야 합니다.
하지만 console 식별자는 스코프 체인에 등록되어있지않고 전역 객체에 프로퍼티로 존재합니다.
이는 전역 객체의 프로퍼티가 마치 전역 변수처럼 전역 스코프를 통해 검색 가능해야 한다는 것을 의미합니다.
2. log 프로퍼티를 console 객체의 프로토타입 체인을 통해 검색합니다.
그 후 console.log()메서드에 인수로 전달된 표현식 a+x+y가 평가됩니다.
3. a,x,y 식별자는 스코프 체인을 통해 검색합니다.
4. console.log()메서드의 실행이 종료되면 함수 코드 실행 과정이 종료되고(console이 함수의 마지막 코드니까?)
함수 호출 이전으로 되돌아가 전역 코드 실행을 다시 진행합니다.
이렇게 foo(100)으로 함수 호출을 한 후
함수 호출이 종료되면 함수 호출 이전으로 되돌아가기 위해 현재 실행 중인 코드와
이전에 실행하던 코드를 구분하여 관리해야 합니다.
코드가 실행되려면 다음과 같은 관리가 필요합니다.
무슨 관리냐고요? 밑에 써놓음ㅋ
🔍자바스크립트 환경에서 코드가 실행되려면?
1. 선언에 의해 생성된 모든 식별자를 스코프를 구분하여 등록하고 상태변화를 지속적으로 관리할 수 있어야함
2. 스코프는 중첩관계에 의해 스코프 체인을 형성해야함 , 즉 스코프 체인을 통해 상위 스코프로 이동하여 식별자를 검색할 수 있어야함
3. 현재 실행 중인 코드의 실행 순서를 변경(함수 호출에 의한 실행 순서 변경)을 할 수 있어야하고 다시 되돌아갈 수도 있어야한다.
언제봐도 참 정의들은 추상적인 것 같습니다.
하여간 저 위의 명제를 모두 관리하는 것이 실행 컨텍스트입니다.
😐실행 컨텍스트는?
소스 코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역
식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘
모든 코드는 실행 컨텍스트를 통해 실행되고 관리됨
식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고 코드 실행 순서는 실행 컨텍스트 스택으로 관리
흠... 이제 처음보다는 덜 추상적인 것 같은데 그래도 모르는 단어가 너무 많으니까 어지럽다그죠
스코프체인은 뭐고 렉시컬 환경은 뭐임?
너무 긴 내용이니까
지금은 그냥 스코프체인 -> 하위는 상위를 참조할 수 있지만 상위는 하위를 참조 못함
즉 함수스코프에 있는 애들은 전역스코프의 변수들을 참조할 수 있지만
전역 스코프는 함수 스코프 안에 있는 애들을 참조할 수 없는것
정도로 생각하고 넘어갑시다
🔍실행 컨텍스트 스택 (콜스택)
스택은 좀 친숙하네요 실행컨텍스트는 스택 자료구조로 관리되고 이를
실행컨텍스트 스택이라고 부릅니다(콜스택이라고도 부른다고합니다.)
스택은 후입선출 LIFO 라고도 부르는데 그럼 맨처음에 들어간게 맨 마지막에 나오게 되는 구조겠네요
자바스크립트 엔진은 보통 먼저 전역 코드를 평가하고 전역 실행 컨텍스트를 생성하니까
스택에서 가장 나중에 뽑혀나오는건 전역 실행 컨텍스트겠네요!
실행 컨텍스트 스택은 코드의 실행 순서를 관리한다.
소스코드가 평가되면 실행 컨텍스트가 생성되고 실행 컨텍스트 스택의 최상위에 쌓인다.
실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행중인 코드의 실행 컨텍스트이다.
따라서 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트를
실행중인 실행 컨텍스트(running execution context)라고 부른다.
다음 예제를 통해 스택에 대한 이해를 해봅시다.
const x = 1
function foo () {
const y = 2;
function bar() {
const z = 3
console.log(x + y + z)
}
bar()
}
foo(); // 6
foo()함수의 실행과정을 실행컨텍스트 스택의 동작과정과 함께 보면 어떨까용
1. 전역 코드를 평가하고 실행
전역 변수 x와 전역함수 foo가 전역 실행 컨텍스트에 등록되고
이후 전역 코드가 실행하기 시작하면 변수 x에는 1이 할당되고
전역 함수 foo()가 호출됩니다. 위에서 이미 다해본거죠? 쉽네용
전역 실행 컨텍스트 |
2. foo 함수 코드 평가
foo가 호출되면 전역 코드의 실행은 일시 중단되고 코드의 제어권이 foo 함수 내부로 이동합니다.
자바스크립트 엔진은 foo 함수 내부의 함수 코드를 평가하고
foo 함수의 실행 컨텍스트를 생성한뒤 실행컨텍스트 스택에 foo함수 실행 컨텍스트를 push합니다.
이때 foo 함수의 지역 변수 y와 중첩함수 bar가 foo함수 실행 컨텍스트에 등록됩니다.
foo함수 실행 컨텍스트 push |
전역 실행 컨텍스트 |
3. foo 함수 코드 실행
이후 foo 함수 코드가 실행되기 시작하며 이 시점에서
지역 변수 y에 값 2가 할당되고 중첩 함수 bar가 호출됩니다.
foo함수 실행 컨텍스트 |
전역 실행 컨텍스트 |
4. bar 함수 코드 평가
중첩함수 bar가 호출되면 foo 함수 코드의 실행은 일시 중지되고 코드의 제어권이 bar 함수 내부로 이동합니다.
bar함수 내부의 코드를 평가하여 bar 함수 실행 컨텍스트를 생성하고 실행컨텍스트 스택에 push 합니다.
이때 bar 함수의 지역변수 z가 bar 함수 실행 컨텍스트에 등록됩니다.
bar함수 실행 컨텍스트 push |
foo함수 실행 컨텍스트 |
전역 실행 컨텍스트 |
5. bar 함수 코드 실행
bar 함수 코드가 실행되기 시작하며 지역 변수 z에 값 3이 할당되고
console.log()메서드를 호출 한 이후 bar 함수는 종료됩니다.
6. foo 함수 코드로 복귀
bar 함수가 종료되면 코드의 제어권은 다시 foo 함수로 이동합니다.
이때 자바스크립트 엔진은 bar 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 pop 합니다.
그리고 foo 함수는 더이상 실행할 코드가 없으므로 종료됩니다.
bar함수 실행 컨텍스트 pop |
foo함수 실행 컨텍스트 |
전역 실행 컨텍스트 |
7. 전역 코드로 복귀
foo 함수가 종료되면 코드의 제어권은 다시 전역 코드로 이동합니다.
이때 자바스크립트 엔진은 foo 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 pop합니다.
foo함수 실행 컨텍스트 pop |
전역 실행 컨텍스트 |
그리고 전역 실행 컨텍스트도 더이상 실행할 코드가 없으므로 pop하고
전역 실행 컨텍스트 pop |
8. 끝
실행 컨텍스트 스택에는 아무것도 남아있지 않게 됩니다.
원래 책은 5단계로 끝이지만 이해를 돕기 위해 단계를 좀 더 쪼개봤습니다.
여기까지만 해도 이미 충분히 어지러우니까
렉시컬 환경 이후의 내용은 포스팅을 쪼개서 다음 글에 올리겠습니다.
감사합니다.
'javascript' 카테고리의 다른 글
요약 정리는 못 참지 않을까요? (1) (1) | 2023.01.06 |
---|---|
자바스크립트 실행 컨텍스트 (execution context) 란? (2) (1) | 2022.12.25 |
유사 배열이 대체 뭐임? Immutability는 뭐임? (0) | 2022.12.20 |
자바스크립트의 프로미스 (1) | 2022.12.20 |
널리쉬 병합 연산자 '??' (Nullish coalescing operator) Javascript (1) | 2022.12.15 |