❗❗실행 컨텍스트는 자바스크립트의 동작 원리를 담고 있는 핵심 개념이다❗❗
실행 컨텍스트를 바르게 이해하면 스코프를 기반으로, 식별자와 식별자에 바인딩 된 값(식별자 바인딩)을 관리하는 방식, 호이스팅이 발생하는 이유, 클로저의 동작 방식, 태스크 큐와 함께 동작하는 이벤트 핸들러와 비동기처리의 동작방식을 이해할 수 있다.
스코프, 식별자, 식별자 바인딩, 클로저, 호이스팅, 태스크 큐, 비동기처리 동작방식을 이해할 수있다
4가지 타입의 소스코드 - 실행 컨택스트 생성 (ECMAScript)
소스코드 타입 | 설명 |
---|---|
전역 코드 | 전역에 존재하는 소스코드, 전역에 정의된 함수, 클래스는 포함하지 않음 |
함수 코드 | 함수 내부에 존재하는 소스코드, 함수 내부에 중첩된 코드, 클래스는 포함되지 않음 |
eval 코드 | 빌트인 전역함수인 eval 함수에 인수로 전달되어 실행되는 소스코드. |
모듈 코드 | 모듈 내부에 존재하는 소스코드, 모듈 내부의 함수, 클래스 등의 내부코드는 포함되지 않음 |
소스코드를 네가지로 분류하는 이유?
📌 소스코드의 타입에 따라 실행 컨택스트를 생성하는 과정과 관리 내용이 다르기 때문이다
✅ 전역코드, 함수코드, eval코드, 모듈코드는 평가 될 때 각각의 실행 컨택스트 생성.
1. 전역 코드
전역 코드가 평가되면 전역 실행 컨텍스트 생성
- 전역 변수를 관리하기 위해. 최상위 스코프인 전역 스코프 생성,
- 전역 객체와 연결 - var 키워드로 생성된 전역변수 - 함수 선언문으로 정의된 전역변수
전역 객체 프로퍼티와 메서드로 바인딩
2. 함수 코드
함수 코드가 평가되면 함수 실행 컨택스트가 생성됨
- 지역 스코프 생성,
- 지역 변수, 매개변수, arguments를 관리
- 생성한 지역스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결
3. eval 코드
eval코드가 평가되면 eval 실행 컨택스트 생성
- strict mode에서 독자적인 스코프 생성
모듈 코드
모듈 코드가 평가되면, 모듈 실행 컨택스트 생성
- 모듈별로 독립적인 모듈 스코프생성
자바스크립트 엔진은 소스코드를 “소스코드의 평가”와 “소스코드의 실행” 두개의 과정으로 나누어 처리한다
소스코드의 평가
- 실행컨택스트 생성
- 변수, 함수등의 선언문만 먼저 실행
- 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록
✅ 소스코드 평가과정실행컨텍스트 생성 → 변수, 함수 선언문만 실행 → 실행 컨텍스트 스코프에 등록
✅ 소스코드평가 이후에 런타임이 실행된다 ( = 선언문 제외 소스코드가 순차적으로 실행된다)
예시
var x;
x = 1;
위 두개의 코드를 처리하는 과정
소스코드의 평가 과정에서
1. 실행컨텍스트 생성,
2. var x;만 먼저 실행,
→ x라는 변수는 실행컨텍스트가 관리하는 스코프에 등록되고, undefined로 초기화됨
3. 소스코드 평가가 끝나면 소스코드 실행 과정이 실행된다
소스코드 실행 과정에서
4. var x;는 이미 소스코드 평가 과정에서 실행완료 되었으므로, 실행 과정에서는 변수 할당문 x = 1;만 실행된다
- 이때 x변수에 값을 할당되려면, x변수가 선언된 변수인지 확인해야함
- 이를 위해 실행컨텍스트가 관리하는 스코프에 x변수가 등록되어있는지 확인한다
5. x가 실행 컨텍스트가 관리하는 스코프에 등록되어있다면?
- x는 평가 과정에서 선언문이 실행되어 등록된 변수
6. x가 선언된 변수라면, 값을 할당하고, 할당 결과를 실행 컨텍스트에 등록해서 관리함
const x = 1;
const y = 2;
function foo(a) {
const x = 10;
const y = 20;
console.log(a + x + y);
}
foo(100);
console.log(x + y);
위와 같은 예제를 자바스크립트 엔진은 어떻게 평가, 실행하는지 살펴보면
1. 전역 코드 평가
- 전역 코드를 실행하기 앞서 전역 코드 평가 과정을 거친다
- 소스코드 평가 과정에서는 선언문만 먼저 실행함
- 전역 코드의 변수 선언문과 함수 선언문이 먼저 실행되고, 그 결과로 생성된 전역변수와 전역 함수가 실행컨텍스트가 관리하는 전역 스코프에 등록
- var 키웓로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 된다
2. 전역 코드 실행
- 전역 코드 평가 과정이 끝나면 런타임이 시작되어 전역 코드가 순차적으로 실행되기 시작함
- 전역변수에 값이 할당되고 함수가 호출됨
- 함수가 호출되면 전역 코드의 실행을 일시 중단하고, 코드 실행 순서를 변경하여 함수 내부로 진입
3. 함수 코드 평가
- 함수 호출로 코드 실행순서가 변경되어 함수 내부로 진입하면, 함수 내부의 문을 실행하기 앞서 함수 평가 과정을 거침
- 매개변수와 지역 변수 선언문이 먼저 실행됨
- 생성된 매개변수와 지역변수가 실행 컨텍스트가 관리하는 지역 스코프에 등록됨
- 함수 내부에서 지역변수처럼 사용되는 arguments 객체가 생성되어 지역 스코프에 등록되고, this 바인딩도 결정됨
4. 함수 코드 실행
- 함수 코드 평가가 끝나면 런타임이 시작되어 함수 코드가 순차적으로 실행됨, 매개변수와 지역변수에 값이 할당되고, console.log 메서드가 호출됨(코드에 console.log)
- console.log 메서드를 호출하기 위해, 식별자인 console을 스코프 체인을 통해 검색한다
- 이를 위해 함수 코드의 지역 스코프는 상위 스코프인 전역 스코프와 연결되어야 한다. console 식별자는 스코프체인에 등록된게 아니라 전역 객체에 프로퍼티로 존재한다 (전역 객체의 프로퍼티가 전역 변수처럼 전역 스코프를 통해 검색 가능해야한다)
- log 프로퍼티를 console 객체의 프로토타입 체인을 통해 검색한다
- console.log 메서드에 인수로 전달된 표현식 a + x + y가 평가된다, a,x,y 식별자는 스코프 체인을 통해 검색함
- console.log 메서드 실행이 종료되면, 함수 코드 실행 과정이 종료되고, 함수 호출 이전으로 되돌아가 전역 코드 실행을 계속한다
⭐️요약
💡 코드가 실행되려면, 스코프, 식별자, 코드실행 순서 등의 관리가 필요하다
- 코드가 실행되려면, 스코프를 구분하여 식별자와 바인딩된 값이 구분되어야 한다.
- 중첩 관계에 의해 스코프 체인을 형성하여 식별자를 검색할 수 있어야 함
- 전역객체의 프로퍼티도 전역 변수처럼 검색할 수 있어야 한다
- 또한 함수 호출이 종료되면, 함수 호출 이전으로 돌아가기 위해 현재 실행중인 코드와 이전에 실행하던 코드를 구분하여 관리해야 함
실행 컨텍스트가 관리하는 것
-
선언에 의해 생성된 모든 식별자(변수, 함수, 클래스)를 스코프를 구분하여 등록하고 상태 변화(식별자에 바인딩 된 값의 변화)를 지속적으로 관리할 수 있어야함
-
스코프는 중첩 관계에 의해 스코프 체인을 형성해야 함
스코프 체인을 통해 상위 스코프로 이동하여 식별자를 검색할 수 있어야함
-
현재 실행중인 코드의 실행순서를 변경할수 있어야한다.
ex) 함수 호출에 의한 실행순서 변경
📌 실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역 스코프 관리(식별자 등록 , 관리)와 실행 순서 관리를 구현한 내부 메커니즘이다.✅ 모든 식별자에 대해 스코프 구분하여 등록, ✅ 변수의상태변화 지속적 관리 ✅ 스코프 체인 형성 ✅ 스코프코드 실행 순서 변경등등
📌 렉시컬 환경 + 실행 컨텍스트 스택
✅ 식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고, 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다
예제로 실행 컨텍스트 스택 이해하기
💡 코드 실행 순서를 결정하는 실행 컨텍스트는 스택 자료구조로 관리된다
전역 코드 평가 - 전역 코드 실행컨텍스트 생성 - 전역코드 실행 - 함수 호출되면 함수 실행 컨텍스트 생성 - 함수 실행 - …. 이런식으로 코드가 실행되는데 실행되는 시간의 흐름에 따라 컨텍스트가 push / pop 된다
const x = 1;
function foo() {
const y = 2;
function bar() {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo();
순서 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
bar함수 실행 컨텍스트 | ||||||
foo 함수 실행 컨텍스트 | foo함수 실행 컨텍스트 | foo함수 실행컨텍스트 | ||||
전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | 전역 실행컨텍스트 | ||
> | > | > | > | > | > | > |
💡 전역코드 { foo 코드 { bar 코드 }}
-
전역 코드 평가와 실행
- 전역 코드를 평가하여, 전역 실행 컨텍스트 생성
- 실행 컨텍스트 스택에 푸시
- 변수 x와 foo는 전역 실행 컨텍스트에 등록된다
- 전역코드 평가가 끝나면 전역 코드 실행
- 변수 x에 값이 할당되고, foo 함수 호출
- 전역 코드를 평가하여, 전역 실행 컨텍스트 생성
-
foo 함수 코드 평가와 실행
- 함수 foo가 호출되면, 전역 코드 실행이 일시 중단되고, 코드 제어권이 foo 함수 내부로 이동함
- 자바스크립트 엔진이 foo 내부 코드를 평가
- foo 함수 실행컨텍스트 생성,
- 실행 컨텍스트 스택에 foo 실행컨텍스트 푸시
- foo 함수의 지역변수 y와 중첩 함수 bar가 foo 함수 실행컨텍스트에 등록됨
- foo함수 평가가 완료되면 foo 함수 실행
- 지역변수 y에 값이 할당되고 중첩변수 bar가 호출됨
-
bar 함수 코드 평가와 실행
- 중첩 함수 bar가 호출되면, foo 함수 코드 실행이 일시 중단되고, 코드의 제어권이 bar 함수 내부로 이동
- 자바스크립트 엔진이 bar 내부 코드를 평가
- bar 함수 실행컨택스트 생성,
- 실행컨택스트 스택에 bar 함수 실행컨택스트 푸시
- bar 함수의 지역변수 z가 bar 함수 실행컨텍스트에 등록
- bar 함수 평가가 완료되면 bar 함수 실행
- 지역변수 z에 값 할당, console.log 메서드 호출 후에 bar 함수 종료
-
foo 함수 코드로 복귀
- bar함수가 종료되면, 코드의 제어권을 다시 foo함수로 이동
- 실행 컨텍스트 스택에서 bar 함수의 실행컨텍스트 pop으로 제거
- foo 함수도 더이상 실행할게 없으므로, 종료됨
- bar함수가 종료되면, 코드의 제어권을 다시 foo함수로 이동
-
전역 코드로 복귀
- foo함수가 종료되면, 코드의 제어권을 다시 전역코드로 이동
- 실행컨텍스트 스택에서 foo함수의 실행컨텍스트 pop으로 제거
- 전역코드도 더이상 실행할게 없으므로 종료,
- 실행컨텍스트 스택에서 전역 실행컨텍스트 pop으로 제거
- foo함수가 종료되면, 코드의 제어권을 다시 전역코드로 이동
💡 실행 컨텍스트 스텍은 이처럼, 코드 실행순서를 관리함, 실행 컨텍스트의 최상위에 있는 실행 컨텍스트는 언제나 현재 실행중인 코드의 실행컨텍스트이다 running execution context라고 부름 (실행중인 실행컨텍스트)
📌 스코프와 식별자를 관리
실행 컨텍스트 스택 - 실행 순서 관리
렉시컬 환경 - 식별자와, 식별자에 바인딩 된 값, 상위스코프 참조 기록
-
렉시컬 환경은 객체 형태의 스코프를 생성
- 키와 값을 갖는 객체형태의 스코프 생성
- 식별자 키로 등록, 식별자에 바인딩 된 값 관리
💡 스코프를 구분하여 식별자를 등록하고 저장소 역할을 한다
💡 렉시컬 스코프의 실체
-
실행 컨텍스트와 렉시컬 환경의 관계
-
컴포넌트 이름 설명 환경 레코드
(Environmental Record)스코프에 포함된 식별자 등록,
식별자에 바인딩 된 값 관리,
소스코드 타입에 따라 관리하는 내용이 다르다외부 렉시컬 환경 참조
(OuterLexicalEnvironmental Reference)상위 스코프를 가리킴
해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 말한다
단방향 링크드 리스트인 스코프체인을 구현함
var x = 1;
const y = 2;
function foo(a) {
var x = 3;
const y = 4;
function bar(a) {
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
-
전역 객체는 전역 코드가 평가되기 이전에 생성됨
전역 객체 생성시 전역 객체에 추가되는 것들 ✅ 빌트인 전역 프로퍼티 ✅ 빌트일 전역 함수 ✅ 표준 빌트인 객체 - (Object, Array, String, Number, Boolean, Map, Set ...) ✅ 호스트 객체 (클라이언트 - Web API)
-
전역 객체도 Object.prototypedmf 상속받는다
- 전역 객체도 프로토 타입 체인의 일원이다
##평가순서
-
소스코드가 로드 된 이후 자바스크립트 엔진은 다음과 같은 순서로 전역 코드를 평가한다
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥 1. 전역 실행 컨텍스트 생성 ↓ 2. 전역 렉시컬 환경 생성 2-1. 전역 환경 레코드 생성 2-1-1. 객체 환경 레코드 생성 2-1-2. 선언적 환경 레코드 생성 2-2. this 바인딩 2-3. 외부 렉시컬 환경에 대한 환경 참조 결정 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
전역 실행 컨텍스트와 렉시컬 환경 생성 결과
1. 전역 실행 컨택스트 생성
- 비어있는 실행 컨텍스트 생성, 실행 컨텍스트 스택에 푸시
2. 전역 렉시컬 환경 생성
- 전역 렉시컬 환경을 생성하고 실행 컨텍스트에 바인딩
2-1. 전역 환경 레코드 생성 🔥❗️
📌 전역 환경 레코드 = 객체 환경 레코드(var) + 선언적 환경 레코드(let, const)
-
전역 스코프(전역변수 관리), 전역 객체의 빌트인 프로퍼티, 빌트인 함수, 표준 빌트인 객체 제공
-
ES6 이전에는 전역 객체가 전역 환경 레코드의 역할을 수행
-
❗️ 전역에서 선언된 let, const 키워드 사용 시 전역적인 프로퍼티가- 아니라 개념적인 블록에 존재하게 되는데..
이러한 관리를 위해 전역 환경 레코드는객체 환경 레코드 + 선언적 환경 레코드로 구성
✅ 기존의 var - 전역 프로퍼티 - "객체" 환경 레코드(Object Environment Record) ✅ const, let - 전역 블록에 존재 - "선언적" 환경 레코드(Declarative Environmental Record)
2-1-1. 객체 환경 레코드 생성
📌 객체 환경 레코드는 BindingObject와 연결, BindingObject는 var 또는 전역 환경에서 function 키워드들을 전역 객체(Window)에 연결
💡 var 키워드, 전역에서의 function이 전역 객체의 프로퍼티와 메서드가 되는 메커니즘
💡 전역 객체 식별자 없이 프로퍼티, 메서드 사용 가능한 이유( window.alert || alert )
🔥🔥 예제 코드 체크
//전역 환경, 객체 환경 레코드 생성 중
var x = 1;
// const y = 2;
function foo() ...
-
var x
- 생성과 초기화 동시에 진행, 변수 따라서 선언문 이전에도 참조 가능 (실행 이전에 평가가 일어나니까 - 호이스팅)
- 생성시 BindingObject를 통해 전역 객체에 변수 식별자 등록, undefined으로 초기화
- var 키워드로 선언한 함수 표현식도 동일하게 동작한다
-
function foo()
- 함수 선언문으로 선언한 함수는 - 함수 이름을 식별자로 BindingObject를 통해 전역 객체에 키로 등록
- 키로 등록함과 동시에 생성된 함수 객체 즉시 할당 * 변수 호이스팅 -> undefined 먼저 할당, 함수 선언문 -> 함수 객체 먼저 할당 - 변수 호이스팅과 함수호이스팅의 차이
-
✅ 객체 환경 레코드 생성결과
2-1-2. 선언적 환경 레코드 생성
📌 let, const는 선언적 환경 레코드에 등록되고 관리된다 전역에서의 let, const는 전역 프로퍼티가 아니라 개념적인 블록 즉 전역의 선언적 환경 레코드 내에 존재
🔥🔥 예제 코드 체크
//전역 환경, 선언적 환경 레코드 생성중
var x = 1;
// const, let 키워드 -> 선언적 환경 레코드에서 생성
const y = 2;
function foo() ...
-
const y
- 전역 객체의 프로퍼티가 되지 않음, 따라서 window.y로 접근 불가능
- 생성과 초기화 분리해서 진행 변수가 undefined로 초기화 되어있지 않으므로 참조시 에러 발생
- 호이스팅은 발생하나, 실제로는 작동하지 않음, 초기화 전까지 일시적 사각 지대 (Temporal Dead Zone)
- 생성시 BindingObject를 통해 전역 객체에 변수 식별자 등록, undefined으로 초기화
-
🔥 TDZ 관련 예제
let foo = 1; 전역 함수 { console.log(foo); let foo = 2; }
- let 키워드로 선언한 변수가 호이스팅 되지 않는다면?
- 전역 변수를 참조해서, 1을 출력해야함
- 하지만 let 변수도 호이스팅이 일어나기 때문에 참조에러가 발생
- 블록 안의 console.log는 블록 안의 foo 참조
- 블록 안의 foo가 현재 TDZ 상태이므로 참조 에러(RefereneError) 발생
- let 키워드로 선언한 변수가 호이스팅 되지 않는다면?
-
✅ 객체 환경 레코드, 선언적 환경 레코드 생성결과
💡 객체 환경 레코드와 선언적 환경 레코드는 협력하여 스코프와 전역 객체를 관리
2-2. This 바인딩
- 전역 환경 레코드의 [[GlabalThisValue]] 내부 슬롯에 this가 바인딩,
- 전역 코드에서 this는 일반적으로 전역 객체를 가리킴, 전역 환경 레코드의 [[GlobalThisValue]]에는 window객체가 바인딩된다.
- this 바인딩은 전역 환경 레코드와 함수 환경 레코드에만 존재함!!
- this 바인딩은 전역환경 또는 함수환경에서만 가능
- ✅ 전역 환경 레코드 this 바인딩 결과
2-3. 외부 렉시컬 환경에 대한 참조 결정
- 현재 평가중인 소스코드 포함, 외부 소스코드의 렉시컬 환경을 가리킴
- 즉 상위 스코프를 가리킴, 단방향 링크드인 스코프 체인 구현
- 현재 평가 중인 소스코드는 전역코드임
- 따라서 전역 렉시컬 환경 - 외부 렉시컬 환경은 null이 할당됨
- 전역 렉시컬 환경이 스코프 체인의 종점에 존재한다@@
- 따라서 전역 렉시컬 환경 - 외부 렉시컬 환경은 null이 할당됨
- ✅ 전역 렉시컬 환경 - 외부 렉시컬 환경 참조 결정 결과
var x = 1;
const y = 2;
function foo()
-
전역 렉시컬 환경 생성 이후 전역 코드가 생성된다
- 변수 할당문 실행, x와 y에 값이 할당된다
- foo 함수가 호출됨
-
🔥식별자 결정
-
변수 할당문, 함수 호출문 실행하기 전에 식별자를 먼저 결정해야 하는 어떤 방식으로 결정되는가?
1. 실행중인 실행 컨텍스트에서 검색 2. 없으면 상위스코프로 이동해서 식별자 검색
-
현재 전역 실행 컨택스트 실행 중이므로, 전역 렉시컬 환경 안의 객체 환경 레코드, 선언적 환경 레코드 안에 x와 y foo가 모두 등록이 되어있다.
-
만약 식별자가 없었다면, 전역에서의 상위 스코프는 null 이므로, 참조 에러를 발생시킨다
-
- 예제 코드
var x = 1;
const y = 2;
function foo(a) {
var x = 3;
const y = 4;
function bar(a) {
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
-
x에 1 할당, y에 2 할당 이후 foo 함수를 실행하게 된다 이때 전역 코드의 실행을 일시 중지하고, foo 함수 내부로 코드의 제어권이 이동된다
-
함수 코드 평가는 아래 순서로 진행된다
1. 함수 실행 컨텍스트 생성 2. 함수 렉시컬 환경 생성 2-1. 함수 환경 레코드 생성 2-2. this qkdlseld 2-3. 외부 렉시컬 환경에 대한 참조 결정
- 함수 실행 컨텍스트 생성
- foo함수 실행 컨텍스트를 생성함
- 함수 실행 컨텍스트는 함수 렉시컬 환경이 완성된 다음 실행 컨텍스트 스텍에 푸시된다
- 이때 foo함수 실행 컨텍스트는 실행 컨택스트 스택의 최상위로 올라감, (실행중인 컨텍스트)
- 함수 렉시컬 환경 생성
-
foo함수 렉시컬 환경(Function Lexical Environment)을 생성하고, foo 함수 실행 컨텍스트에 바인딩한다
-
함수 렉시컬 환경은 함수 환경 레코드 + 외부 렉시컬 환경에 대한 참조로 구성됨!! (기본 렉시컬 환경 구성 = 환경 레코드 + 외부 렉시컬 환경 참조)
2.1 함수 환경 레코드 생성
📌 함수 렉시컬 환경 - 함수 환경 레코드 생성할 때
매개변수, arguments 객체, 함수 내부의 지역변수와 중첩 함수 등록 및 관리
- argumetns / foo 함수 내의 x, y, bar함수 / callee - 함수 환경 레코드에 등록
📌 this는 함수 호출 방식에 따라 결정된다. 함수 호출 시에 함수 렉시컬 환경 내의 [[ThisValue]]에 바인딩된다
- 22장 this에 설명 되어있음
- 요약
- 일반함수 - 전역 객체 바인딩
- 객체 메서드 - 메서드를 호출한 객체 바인딩
- 생성자 함수 - 미래의 인스턴스 바인딩
- apply,call,bind - 첫번째 인수로 전달해준 객체
- 요약
📌 지금 실행하는 함수 "정의"가 평가된 시점의 실행컨텍스트의 렉시컬 환경의 참조가 할당된다 함수의 외부 렉시컬 환경 참조 함수가 정의된 행 컨텍스트의 렉시컬 환경을 참조
- foo가 평가될 시점에서의 실행 컨텍스트는 전역 실행 컨텍스트이므로, 전역 렉시컬 환경을 참조함.
- 상위 스코프 결정 시 외부 렉시컬 참조를 활용한다
- 함수 객체 생성 시 함수의 상위 스코프 (= 외부 렉시컬 환경 참조, =함수 정의가 평가된 실행 컨텍스트의 렉시컬 환경)를 함수 객체의 내부슬롯 [[Environment]]에 저장한다.
23.6.5. foo 함수 실행
-
런타임 시작, 매개변수에 할당, 변수 할당 등 지역변수에 값이 할당된다 bar 함수 호출됨
-
식별자 결정을 할 때 함수 렉시컬 -> 외부 렉시컬 참조 순으로 식별자를 검색하고 값을 바인딩한다
23.6.6~7 bar 함수 코드 평가/실행
-
var x = 1;
const y = 2;
function foo(a) {
var x = 3;
const y = 4;
function bar(a) {
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
-
foo 함수를 평가 / 실행 한 것처럼 동일하게 동작
- bar 함수 호출 시 내부 제어권 bar 함수로 이동
- 함수 평가
- bar) 함수 실행 컨텍스트 생성 - 함수 렉시컬 환경 생성 - 환경 레코드 등록 - this 바인딩 - 외부 렉시컬 환경 참조 순으로 평가
- 외부 렉시컬 환경은, bar 함수 평가가 실행되는 foo 함수의 렉시컬 환경,,, 정의된 함수의 렉시컬 환경이라고 생각하면 될듯 하다
- bar) 함수 실행 컨텍스트 생성 - 함수 렉시컬 환경 생성 - 환경 레코드 등록 - this 바인딩 - 외부 렉시컬 환경 참조 순으로 평가
- 함수 실행
- z에 값 할당 이후 console.log 실행
-
console.log 실행
-
console 식별자 스코프 체인에서 검색한다
- 현재 실행중인 렉시컬 환경 ->외부 렉시컬 ->외부 렉시컬 반복해서 console 식별자를 찾는다
- bar 렉시컬 없음 -> foo 렉시컬 없음 -> 전역 렉시컬 -> 객체 환경 레코드의 BindingObject에서 찾게 됨
- 현재 실행중인 렉시컬 환경 ->외부 렉시컬 ->외부 렉시컬 반복해서 console 식별자를 찾는다
-
log 메서드 검색
- console 객체의 프로토타입 체인을 통해 메서드를 검색
-
표현식 (a,b,x,y,z) 평가
- 렉시컬 -> 렉시컬 계속 참조하면서 식별자 a,b,x,y,z 를 계속해서 찾는다
- 찾은 이후 식을 평가하여 인수로 전달해준다.
-
log 메서드 호출
-
표현식 평가 이후 생성된 값을 console.log에 전달하여 호출함
23.6.8 bar 함수 코드 실행 종료
-
-
실행 컨텍스트에서 bar 함수가 종료되어도 bar 함수의 렉시컬 환경은 즉시 소멸하지 않음
- 실행 컨텍스트에 의해 참조되지만 렉시컬 환경은 독립적인 객체이므로, 누군가에 의해 참조되지 않을 때 가비지 컬렉터에 의해 소멸
-
bar 함수가 종료되어도, 누군가 bar 렉시컬 환경을 참조한다면 소멸되지 않는다
23.6.9 foo 함수 코드 실행 종료 / 23.6.10 전역 코드 종료
-
-
각각의 함수는 실행이 종료되면서 실행 컨텍스트 스택에서 팝 되어 제거된다,
-
전역 코드 까지 종료되면 실행 컨텍스트 스택에는 아무것도 남지 않는다.
📌 if / for / while / try, catch 문에서는 블록 렉시컬 환경을 생성한다. 이때 생성된 블록 렉시컬 환경이 블록 레벨 스코프 메커니즘을 가능하게 한다
- 코드 블록이 실행 되었을 때, 블록 레벨 스코프 생성을 위해 렉시컬 환경 새롭게 생성
- 선언적 환경 레코드를 갖는 렉시컬 환경이 생성된다.
- 외부 참조 렉시컬 환경은 코드블록이 실행된 렉시컬 환경을 참조한다.
- 코드블록이 실행 될 때마다 렉시컬 환경을 생성
- for문의 경우 매 반복마다 독립적인 렉시컬 환경을 생성, 식별자 값 유지
- 24장 클로저에 내용이 많다.
- 코드블록 실행이 종료되면, 이전의 렉시컬 환경으로 돌아감
- var 변수는 함수 코드블록만 지역 스코프로 인정, let, const 변수는 모든 코드 불록 지역 스코프로 인정
`