● 실행 컨텍스트의 생성과 식별자 검색 과정
자 일단 이론은 공부했다. 건설업자로 실전에 투입되어보자.
// 전역 변수 선언
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
● 전역 객체 생성
전역객체는 전역 코드가 평가되기 이전에 생성된다. 전역 객체에는 빌트인 전역 프로퍼티와 빌트인 전역 함수 표준 빌트인 객체가 추가되고
동작환경(클라이언트 사이드 또는 서버 사이드)에 따라 클라이언트 사이드 Web API 또는특정 환경을 위한 호스트 객체를 포함한다.
그니까 맨처음에 일단 이 자바스크립트가 돌아가게끔 사용할 수 있는 기본 세팅을 한단 소리다.
● 전역 코드 평가
소스코드가 로드되면 자바스크립트 엔진은 전역 코드를 평가한다. 전역 코드 평가는, 위에 나와있는 목차 그대로 진행된다.
● 전역 실행 컨텍스트 생성
먼저 비어있는 전역 실행 컨텍스트를 생성하여 실행 컨텍스트 스택에 푸시한다. 이때 전역 실행 컨텍스트는 실행 컨텍스트 스택의 최상위, 즉 실행 중인 실행 컨텍스트가 된다.
즉, 일단 코드 로드 됐으니까, 지금 건설업자인 자바스크립트가 부지 보러나온거다. 일단 건물지을라면 자재뭐가 필요한지 리스트 적어볼까?
● 전역 렉시컬 환경 생성
전역 렉시컬 환경을 생성하고, 전역 실행 컨텍스트에 바인딩한다.
렉시컬 환경에서 살펴보았듯, 렉시컬 환경은 2개의 컴포넌트인 환경레코드와 외부 렉시컬 환경에 대한 참조로 구성된다.
야 리스트 종이이랑 펜 가져왔냐? 일단 자재적을데랑(변수,함수) 이거 어느 부지꺼인지(위치,구역) 적을 공간 만들어
● 전역 환경 레코드 생성
ES6 이전에는 모든 전역 변수가 전역 객체의 프로퍼티가 되었다. let const가 없었으니까, 전역스코프에서 선언한 모든 변수들이 전역 객체의 프로퍼티가 되었다는 것임.. 개노답;; 변수가 겁나게 가변적이여서 안정성이 매우 떨어졌다고 보면될 것 같다. 그리고 전역객체들은 기본으로 설정되는 프로퍼티들이 개많은데, 거기다가 내가 코드로 적은 전역변수들도 달리게 되었다니.. 너무 복잡하고 안정성이 매우 떨어졌다고 생각하면 된다! 즉, ES6이전에는 전역객체(window 등)가, 전역변수들의 기록장으로 사용되었다는 뜻이다. (= 전역 환경 레코드의 역할을 수행했다.)
이에 대해 let, const 변수가 생긴 이후로 전역 객체가 아닌 다른 곳에 이 변수들을 저장하기 시작했다. 쉽게말하면 var가튼 넘을 구분하고 싶어서 그런 애들은 객체 환경 레코드에, let const 같은 애들은 선언적 환경 레코드에서 관리로 했다.
야, 또라이같은 var 이랑 함수자재는 객체환경 레코드라는데에 적고, 나머지 괜찮은 애들은 선언적 환경에 다 적어
객체 환경 레코드 | 선언적 환경 레코드 |
var 키워드로 선언된 전역변수 | let, const 로 선언된 전역 변수 |
함수선언문으로 선언된 전역함수 | |
표준 빌트인 객체 | |
빌트인 전역 프로퍼티와 빌트인 전역 함수 |
- 객체 환경 레코드 생성
객체 환경 레코드는 BindingObject 라고 부르는 객체와 연결된다. BindingObject 는 전역 객체 생성에서 생성된 전역 객체로 BindingObject 를 통해 전역 객체의 프로퍼티와 메서드가 된다. 그리고 이때 등록된 식별자를 전역 환경 레코드의 객체 환경 레코드에서 검색하면 전역 객체의 프로퍼티를 검색하여 반환합니다.
즉, BindingObject이라는 전역객체로 전역객체의 변수와 함수로 탄생이 된다는 것이다. 그래서 우리는 var같은 변수를 불러 올때 그냥 변수이름으로 불러올 수 있다. 실은 앞에 window가 생략되어 있는거임.
var x ;
x = 1;
function func(){ console.log("hello")}
window.x
window.func
x 는 var 키워드로 선언했기 때문에 선언과 동시에 초기화 단계가 진행된다. BindingObject 를 통해 전역 객체에 식별자를 키로 등록하고 암묵적으로 undefined 가 바인딩된다. (변수 호이스팅이 이래서 일어난다.) 함수 선언문으로 정의한 함수는 평가되면 이름과 동일한 식별자를 BindingObject 를 통해 객체에 키로 등록하고 생성된 함수 객체를 즉시 할당한다. 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출이 가능합니다. (함수 호이스팅)
- 선언적 환경 레코드 생성
let,const 로 선언된 전역 변수는 선언적 환경 레코드에 존재하게 된다. let, const 키워드는 전역 객체의 프로퍼티로서 참조가 불가능하다. 위 그림에서 변수 y에 바인딩되어 있는 <uninitialized>는 초기화 단계가 진행되지 않아 변수에 접근할 수 없음을 나타내기 위해 사용한 표현이다. 이건 TDZ(Temporary Dead Zone) 개념인데 이건 걍 검색하셈
걍 할당문 뜨기 전에는 var처럼 undefined도 아니고 uninitialized로 설정해놓은거임. let const 는 호이스팅이 안되는 것처럼 보이는데 호이스팅 되는거 맞고 선언문 전에 부르면 ReferenceError가 일어남. 근데 호이스팅되는거맞음. 그거는 아래에 포스팅해놓음
- this 바인딩
전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩된다. 일반적으로 전역 코드에서 this는 전역 객체를 가리키므로 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에는 전역 객체가 바인딩된다. 일단 이렇게 알고 넘어가자.
this놈은 또 나중에 조져줄예정이다.
● 외부 렉시컬 환경에 대한 참조 결정
아따.. 자재리스트(변수리스트) 적기 까다롭구만.. var이나 함수같이 까다로운 놈들은 객체 환경 레코드에, 나머지 const let 같은 애들은 선언적 환경 레코드에 적으라는 거지맹..? 이제 그럼 이 자재가 어디있는지 적어줘야대는 거아녀?
외부 렉시컬 환경은 현재 평가 중인 소스코드를 포함하는 외부 소스코드의 렉시컬 환경, 즉 상위 스코프를 가리킨다. 이를 통해 단방향 링크드 리스트인 스코프 체인을 구현한다.(긍까 변수 검색할때 계속 위로 올라간다는겨) 현재 평가중인 애는 전역코드니께.. 상위 스코프 모여 전역코드 포함하는 상위스코프같은 없어. 그러니까 null로 넣어주는겨
자바스크립트는 함수를 어디서 호출했는지가 아니라 어디에 정의했는지에 따라 상위 스코프를 결정한댄다.
그리고 함수 객체는 자신이 정의된 스코프 즉 상위 스코프를 기억한대.
● 전역 코드 실행
아따.. 자재리스트 다 적었네.. 그럼 얘네 몇개필요한지 상세히 기술해야될거아녀.. 그니까 소스실행하는겨.
var x = 1
const y = 2
function foo(a) {
var x = 3
const y = 4
function bar (b) {
const z = 5
console.log(a + b + x + y + z)
}
bar(10)
}
foo(20)
자. var x 부터 시작하는겨.. 지금 실행 컨텍스트 스택에 뭐있어. 선입후출 기억나는겨? 전역실행 컨텍스트여 이눔아. 지금 실행 컨텍스트 스택에 있는게 전역실행컨텍스트박에 업는겨.
1) 그니까 지금 전역실행 컨텍스트에 바인딩된 렉시컬 환경 기억나니?(=변수리스트) 거기에서도 객체 환경 레코드에 내가 적어놓은 적 잇엇나? ㅇㅇ 잇엇네. 이눔 잇엇어. 그럼 x =1 이라고 1개라고 다시 적는겨..
2) y눔은 선언적 환경 레코드에 내가 적어놧는가? 어ㅜ잇엇네 잇엇어. 그럼 2로 적는겨 y놈은 2개라고..
3) 어 뭐시여 foo이라는 함수네 이거 내가 객체 환경 레코드에 적어논겨? 어 잇엇ㅆ네 잇엇어.. 이눔 함수객체 적어봐.
4) 그러고 쭉 내려가다가 foo(20)을 마주함.
5) foo 함수 소스 평가하고 실행하러가자..
그럼 그 이후로 함수 실행 컨텍스트 생성 > 함수 렉시컬 환경 생성 > 함수 환경 레코드 생성 + this 바인딩, 외부 렉시컬 환경 생성
근데 갑자기 bar(10) 호출됨. 그래서 또 위에 있는 상황 반복.. 끝남.
그럼 실행컨텍스트 스택에 bar 함수실행컨텍스트 사라짐
그다음 실행컨텍스트 스택에 foo 함수 실행컨텍스트 사라짐
그다음 실행컨텍스트 스택에 전역함수 실행컨텍스트 사라짐. 끝!
● 실행 컨텍스트와 블록 레벨 스코프
다음 코드를 통해 블록 레벨 스코프에서 실행 컨텍스트가 어떻게 생성되는지 알아보자.
let x = 1;
if (true) {
let x = 10;
console.log(x); // 10
}
console.log(x); // 1
if 문의 코드 블록이 실행되면 if 문의 코드 블록을 위한 블록 레벨 스코프를 생성해야 한다. 이를 위해 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성하여 전역 렉시컬 환경을 교체한다. 이 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 if 문이 실행되기 이전의 전역 렉시컬 환경을 가리킨다.
if 문 코드 블록의 실행이 종료되면 if 문의 코드 블록이 실행되기 이전의 렉시컬 환경으로 되돌린다. 이는 if 문뿐 아니라 블록 레벨 스코프를 생성하는 모든 블록문에 적용된다