현재 어떤 상태에서 수행되고 있는지 정확히 파악하고자 함.
컨텍스트 === 문맥.
OS에서 프로세스도 컨텍스트를 가지듯, JS도 자신만의 컨텍스트를 가진다.
이를 실행 컨텍스트라 함.
js의 동작원리를 담고있는 핵심 개념
이를 알면
위와같은 JS의 본질적인 기능들을 보다 잘 이해하고 알수 있다.
이 파트에서 사용하는 용어중 생소한 용어들을 먼저 소개하고 넘어가겠다.
JS는 소스코드를 이렇게 4종류로 나누었다
나누는 기준은 실행 컨텍스트를 생성하는 과정과 관리가 다르기 때문
전역코드
=> 전역스코프 생성해야함. var
로 선언된 전역변수와 함수선언문 전역객체에 할당작업.
이를 위해 전역코드가 평가될 때 전역 실행 컨텍스트가 생성된다.
함수 코드
=> 지역스코프 생성. 매개변수,arguments
객체 등 관리. 지역스코프를 스코프체인에 할당 등.
이를 위해 함수코드가 평가되면 함수 실행 컨텍스트 생성
eval 코드
=> strcit mode
에서 독자적 스코프 생성. 이를 위해 eval 실행 컨텍스트 생성
모듈 코드
=> 모듈은 독립적인 스코프를 가짐. 따라서 모듈 실행 컨텍스트생성.
4가지의 방식으로 미루어보아, 스코프가 생성되면 실행 컨텍스트도 실행되는 것 같다.
앞서, 평가되면 실행 컨텍스트가 생성된다 하였다.
여기서 평가는 코드를 실행하기위한 준비다.
=> JS는 소스 평가 - 소스 실행2가지 단계를 거친다!
소스코드 평가: 실행 컨텍스트 생성. 변수, 함수등 선언문만 먼저 실행(식별자 등록때문)하여 실행 컨텍스트가 관리하는 스코프(Lexical Environment Record 이하 L.E.R)에 할당.
전에 var, const, let
설명하는 과정에서 const, let
은 L.E.R에 할당된다고 들었는데, 여기구나!
소스코드 실행: 소스코가 순차적으로 실행. 이때 필요한 정보들(변수, 함수의 참조)등을 실행 컨텍스트가 관리하는 스코프에서 검색하여 취득한다! 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록된다.
=> 아하, 결국 실행컨텍스트와 스코프는 밀접한 관련이 있구나
예제로 한번 알아보자.
var x;
x = 1;
소스 평가과정에서 var x;
를 선언하고 실행컨텍스트가 관리하는 스코프에 저장(undefined로 초기화)
평가과정이 끝나면, 실행이 시작된다!
But!!!, var x
는 이미 평가과정에서 끝났다. 따라서 실행과정은 x = 1;
만 실행한다!
내가 보기엔 저장소, OS 프로세스의 컨텍스트와 굉장히 유사한 개념이다.
어떤 상황인지 알아야 판단하고, 어떤 변수가 있는지 참조하는 등.
일단 예제로 알아보자
//전역변수 선언
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
arguments
객체 생성 및 this
바인딩console.log
메소드 호출하기위해 console
을 스코프 체인 통하여 검색. But, 스코프체인에 없음(전역객체 프로퍼티는 그냥 쓸수있다).console
객체의 프로토타입 체인을 타며 log
메소드 검색.a + x + y
평가.이처럼 코드가 잘 진행되려면, 스코프, 식별자, 코드 실행 순서 등의 관리가 필수임
=> 총망라하는게 실행 컨텍스트구나!
식별자, 스코프는 실행컨텍스트의 Lexical Enviroment로 관리하고, 코드실행은 실행컨텍스트 stack으로 관리한다.
=> 마치 함수가 쌓이는 call stack과 유사하군! 함수, 실행컨텍스트라는 쌓이는 본체가 다르지만.
코드 실행은 실행 컨텍스트 스택으로 관리한다 하였다.
다시말해, 어떠한 함수로 진입할시 컨텍스트가 스택처럼 쌓인다
const x = 1;
function foo() {
const y = 2;
function bar() {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo(); //6
전역 실행컨텍스트 => foo가 실행되어 foo실행 컨텍스트 => 내부에서 bar가 실행되어 bar실행컨텍스트 => 실행되고 빠져나감. => foo가 실행되고 빠져나감 => 전역실행컨텍스트 => 종료
어휘적 환경. 참조에 대한 변수들을 저장한 곳이라고 보면 된다.
식별자, 식별자에 바인딩된 값, 상위스코프에 대한 참조가 기록되어있다.
위의 예제에서 foo()
는 상위스코프(전역)에 대한 참조를 갖고있어 x
값을 알 수 있다.
렉시컬 환경은 두개로 또 나뉜다..!
예제를 또..보고싶은데, 너무 길다.
과정만 짚고 넘어가보자.
여기서 전역 E.R생성부터 파헤쳐보자
전역 E.R
본래 전역객체였음. 하지만 const, let
으로 선언한 변수는 전역객체에 들어가지 않음
따라서 전역E.R내부에 Object Environment Recod, Declarative Environment Record로 나뉨.
각각 기존 전역객체가 관리하던 것과, let,const
관리 용도다.
객체E.R
1번에서 설명한 전역객체다. window없이 프로퍼티를 기술할수 있는게 이친구 덕분이다.
호이스팅은 여기서 일어난다.
선언적 E.R
1번에서 설명한 let,const
를 위한 환경이다.
선언,초기화단계가 분리되어 진행됨. TDZ에 빠진다.
this 바인딩
전역 E.R 내부슬롯 [[GlobalThisValue]]
에 this
가 바인딩 된다.
일반적으로 전역코드에서 this
는 전역 객체.
참고로 2,3번에는 this
바인딩이 없다.
외부 L.E에 대한 참조 결정
상위 스코프를 가리킴. 전역에서 외부 L.E는 null이다.
전역 코드 실행
스코프체인은 사실외부L.E를 뒤적이는 작업.
함수는 호출 위치가 아닌 정의된 위치의 상위 L.E를 참조한다.
=> 클로저를 이해할 수 있는 중요한 단서다! 참조하는동안 G.C가 지우지 않기에 외부함수의 변수를 사용한다.
하지만 this
는 호출한 방식에 따라 결정된다! 이를 헷갈리지 말자.
블록레벨 스코프는 결국 블록 내부의 L.E를 새롭게 생성하여, 기존의 전역 L.E를 교체한다.
이때 블록 내부의 외부L.E참조는 당연히 실행 직전 환경이다.
내부적인 요소를 확실히 알아간다.
특히 호이스팅과 클로저 스코프 체인등의 깊은 내용의 동작원리를 완벽히 이해하게 됐다.
정말 너무 지엽적이고 쓸모없어 보였는데, 막상 공부해보니 Js를 더 잘 알게되었다.
이제 this
나 node
조작등만 더 공부하면 이론은 완벽하겠군!
이젠 실전을 많이해보자!