들어가기
- 실행 컨텍스트란 자바스크립트 코드가 실행되고 연산되는 범위를 나타내는 추상적인 개념으로 코드가 실행된다면 실행 컨텍스트 내부에서 실행되고 있는 것이다.
- 자바스크립트 엔진은 코드를 실행하기 위해서 여러가지 정보를 알아야 한다.
- 변수(전역, 지역, 매개 변수, 객체의 프로퍼티), 함수 선언, 변수의 유효범위, this
- 실행 컨텍스트의 타입
- Global Execution Context: window 오브젝트인 전역 컨텍스트를 생성하고, this를 global object로 할당한다.
- Functional Execution Context : 함수가 호출될때마다 실행 컨텍스트가 생성된다.
- Eval Functional Execution Context: eval 함수 또한 자신 만의 실행 컨텍스트를 가진다.
- Execution Stack(호출 스택)과 함수 실행 순서
- 자바스크립트 엔진이 script tag를 처음 만나면 전역 컨텍스트를 만들고 현재 실행되고 있는 호출 스택에 이를 push한다.
- Execution Context 형태
- LEC(Lexical Environment Component): Identifier-variable mapping 되는 곳으로 아래 3가지 일을 진행한다.
- Environment Records: 함수와 변수를 기록한다. (ER)
- DER(Declarative Environment Record) : 변수와 함수 선언을 저장하는 곳이다.
- OER(Object Environment Record): 전역 코드에 대한 LE는 OER에 포함된다.
- Reference to the outer environment: 외부 lexical 환경으로 접근할 수있다는 의미이다. (OLER)
- Environment Records: 함수와 변수를 기록한다. (ER)
- VEC(Variable Environment Component): LEC와 function, 변수 식별자가 binding 되는 점을 포함해 동일하다.
-
TBC(This Binding Component): Thid binding, this의 값이 여기서 결정된다.
/* show 실행 콘텍스트(EC): { 렉시컬 환경 컴포넌트(LEC): { 환경 레코드(ER): { 선언적 환경 레코드(DER):{ title: 'Javascript book' }, 오브젝트 환경 레코드(OER): {} }, 외부 렉시컬 환경 참조(OLER):{ point: 123, getPoint: function(){} } }, 변수 환경 컴포넌트(VEC): {} this 바인딩 컴포넌트(TBC):{ 글로벌 오브젝트(window) } } */ function book() { var point = 123; function show() { var title ="Javascript book"; // getPoint(); // this.bookAmount; } function getPoint() {return point;} show(); }; book();
- book() 함수가 호출되면
- show function 오브젝트 생성
- show의 [[Scope]] 에 스코프 설정 (대괄호 2개는 엔진이 설정하는 프로퍼티를 뜻한다.)
- 스코프는 book() 함수 내로 설정한다.
- show() 함수가 호출되면 functional EC 생성
- 엔진 컨트롤이 show()함수로 이동하기 전에 함수 실행을 위한 Context 환경 구축
- LEC, VEC, TBC 생성 첨부
- LEC에 ER, OLER 첨부
- ER에 DER, OER 첨부
- DER에 show()의 변수, 함수 기록
- OLER에 show()의 [[Scope]] 를 설정
- this 바인딩 컴포넌트에 this 참조 설정
- book() 함수가 호출되면
- LEC(Lexical Environment Component): Identifier-variable mapping 되는 곳으로 아래 3가지 일을 진행한다.
- 식별자 해결 (Identifier Resolution)
- 식별자 해결
- 사용할 변수/함수를 결정하는 것 예) point 변수
- 신속, 정확한 검색을 위해 스코프 필요
- 스코프에서 이름을 찾기 위해
- 스코프 이름을 설정
- 값은 변경되지만 이름은 변경되지 않음
- 식별자 해결 대상은 이름
- resolutation 의 사전적 의미: 해결, 결정
- 결정도 시맨틱적으로 맞음
- 시멘틱: 코드 조각의 의미를 나타낸다.
var point = 100; function getPoint() { var point = 200; return point; } var result = getPoint(); // scope이 getPoint 내부에 있기 때문에 point/200값을 return 한다. console.log(result); // 200
- 식별자 해결
- 스코프 용도
- 식별자 해결을 위한 수단, 방법
- 스코프가 목적이 아니다.
- 식별자가 유일하면 스코프는 필요하지 않다. 하지만 유일하게 작성하는 것은 불가능하기 때문에 스코프가 필요하다.
- 식별자 해결을 위한 수단, 방법
- Scope Chain(ES3)
- 실행 콘텍스트와 관련이 있으며 식별자 해결을 위해 사용
- 식별자를 검색하기 위한 {name: value} 리스트
- 함수가 호출되면 scope를 생성하고 함수의 변수와 함수를 {name: value} 형태로 설정
- 생성한 scope를 scope chain에 연결하고 scope chain에서 식별자를 해결
- 동적 처리
- ES3의 실행 콘텍스트 환경 (scope chain, Artivation Object)
- ES5에는 LEC의 DER에 함수의 변수와 함수 이름을 바인드. ES5부터 Scope chain을 사용하지 않으며 DER에서 변수와 함수이름을 검색하여 실행
- ES3에서는 함수가 호출될 때마다 scope chain을 생성하여 동적으로 처리하는 데, ES5는 정적으로 미리 설정하기 때문에 엔진 처리가 ES5가 빠르다.
- Lexical Environment
- 정적 환경
- function 키워드를 만나면 function 오브젝트를 생성하고 스코프를 FO의 [[Scope]에 설정한다.
- 이것은 함수 밖의 스코프가 설정되는 것. 아직 함수 내부를 들어가지 않았기 때문에 함수 밖의 스코프가 결정된다.
- 그래서 동적이 아닌 정적이다.
- 이 시점에서 스코프가 결정된다. -> 이것이 Lexical Environment (정적 환경) (함수가 호출될 때 생성되는 것이 아니다!!)
- 함수가 호출되면 FO의 [[Scope]]를 실행 콘텍스트의 렉시컬 환경 컴포넌트의 외부 렉시컬 환경 참조에 설정
- function 키워드를 만나면 function 오브젝트를 생성하고 스코프를 FO의 [[Scope]에 설정한다.
- var 키워드 문제
- 함수에서 var 키워드를 사용하지 않고 변수를 선언하면 글로벌 오브젝트에 설정된다. 이는 렉시컬 환경 구조에 맞지 않다.
- ES5에 해결 방법으로 use strict 사용한다.
- Strict mode: ES5 버전에 새로운 기능으로 엄격한 운용 콘텍스트 안에서 실행하게 끔 만들어 준다.
- 장점 1. 흔히 발생하는 코딩 실수를 잡아내어 예외를 발생시킨다.
- 장점 2. 상대적으로 안전하지 않은 액션이 발생하는 것을 방지하거나 그럴 때 예외를 발생시킨다.
- 장점 3. 혼란스럽거나 제대로 고려되지 않은 기능들을 비활성화 시킨다.
- Strict mode: ES5 버전에 새로운 기능으로 엄격한 운용 콘텍스트 안에서 실행하게 끔 만들어 준다.
- ES6 해결 방법으로는
- let 변수, const 변수를 사용한다. 변수 자체에 스코프 제약을 둔다.
- ES6는 기본으로 Strict mode를 사용한다.
- 동적 환경
- 실행 시점에 스코프 결정
- with 문 : strict 모드에서 에러 발생
- eval() 함수: 동적변수를 선언할때 eval 함수를 사용한다. 보안에 문제가 있다.
- 실행 시점에 스코프 결정
- 정적 환경
Execution Context
- Execution Context
- 함수가 실행되는 영역, 묶음
- 함수 코드를 실행하고 실행 결과를 저장
- 스펙 상의 사양 (엔진 처리 과정)
- 실행 콘텍스트 스펙
function music(title) { var musicTitle = title; } music("음악");
- music(“음악”); 으로 함수가 호출하면 엔진은 실행 콘텍스트를 생성하고 실행 컨텍스트 안으로 이동합니다.
- 실행 컨텍스트 실행 단계
- 준비 단계, 초기화 단계, 코드 실행 단계
- Execution Context 생성 시점: 실행 가능한 코드를 만났을 때
- 실행 가능한 코드 유형: 함수 코드, 글로벌 코드, eval 코드
- 코드 유형을 분리한 이유
- Execution Context에서 처리 방법과 실행 환경이 다르기 때문이다.
- 함수 코드 -> Lexical 환경, 글로벌 코드 -> Global 환경 , eval 코드 -> 동적 환경
- Execution Context 상태 컴포넌트
- Execution Context 상태를 위한 오브젝트 -> Execution Context 안에 생성
- 상태 컴포넌트 유형
- Lexical Execution Component(LEC)
- Variable Execution Component(VEC)
- This Binding Component(TBC)
Execution Context(EC): { Lexical Execution Component(LEC): { Environment Record(ER): { point: 100 }, Object Lexical Environment Reference(OLER): { title: "book", getTitle: function() {} } } Variable Execution Component(VEC): {} This Binding Component(TBC): {} }
- Lexical Execution Component
- 함수와 변수의 식별자 해결을 위한 환경 설정
- 함수 초기화 단계에서 해석한 함수와 변수를 {name: value} 형태로 저장, 이름으로 함수와 변수를 검색하게 된다.
- 초기화 단계에서 함수 선언문은 {name: function object} 형태 / 변수, 함수 표현식은 {name: undefined} 형태
- 함수 밖의 함수와 변수 참조 환경을 설정한다. 함수 밖의 함수와 변수를 사용할 수 있게끔 한다.
- function, with, try-catch를 만났을 때 Lexical Execution Component 생성
- 컴포넌트 구성
- Environment Record
- Environment Record에 함수안의 함수와 변수를 기록
- Object Lexical Environment Reference에 function object의 [[Scope]]를 설정
- 따라서 함수 안과 밖의 함수와 변수를 사용할 수 있게 됨
- Object Lexical Environment Reference
- Scope와 실행중인 함수가 Context 형태이므로 스코프의 변수와 함수를 별도의 처리 없이 즉시 사용할 수 있다.
- Execution Context에서 함수 안과 밖의 함수, 변수를 사용할 수 있으므로 함수와 변수를 찾기 위해 Execution Context를 벗어 나지 않아도 된다.
- Environment Record
- Variable Execution Context
- Execution Context 초기화 단계에서 LEC와 함께 설정, 초기값 복원할 때 사용하기 위해 함께 설정된다.
- 코드 실행되며 변경되는 값들은 LEC에 저장되고 초기값은 VEC에 저장된다. VEC에 있는 초기값은 초기화할 때 사용되며 with 문에서 사용된다.
- 함수 코드가 실행되면 실행 결과를 LEC에 설정하며 초기값이 변하게 되므로 이를 유지하기 위한 것이 된다.
-
Execution Context 실행 과정
var base = 200; function getPoint(bonus) { var point = 100; return point + base + bonus; } console.log(getPoint(50)); // 350 출력
- getPoint function object 생성
- 오브젝트의 [[Scope]]에 Global Object 설정
- base 선언
- base에 200 할당 한 후, getPoint()함수 호출
- 엔진은 EC를 생성하고, EC 안으로 이동합니다.
- 준비단계 1. Component를 생성하여 EC에 첨부. LEC, VEC, TBC
- 준비단계 2. ER을 생성하여 LEC에 첨부 - 함수 안의 함수, 변수를 바인딩한다.
- 준비단계 3. OLER을 생성하여 LEC에 첨부하고, function object의 [[Scope]]를 설정
EC: { LEC= { ER: {}, OLER: { base: 200 } }, VEC: { }, TBC: {} }
- 초기화 단계 1. 호출한 함수의 파라미터 값을 호출된 함수의 파라미터 이름에 매핑. 환경 레코드에 작성 (변수 선언 전 파라미터가 먼저 설정된다.)
- 초기화 단계 2. 함수 선언문을 function object로 생성
- 초기화 단계 3. 함수 표현식과 변수에 초기값 설정. 현재까지는 외부에 실행 상태를 제공하지 않는다.
EC: { LEC= { ER: {bonus: 50, point: undefined}, OLER: { base: 200 } }, VEC: { }, TBC: {} }
- 실행 단계 1. 함수 안의 코드를 실행합니다. var point = 100;
- 실행 단계 2. EC 안에서 관련된 함수와 변수를 사용할 수 있습니다.
- 해당 과정은 메모리에서 진행한다.
- getPoint function object 생성
- Environment Record(환경 레코드)
- 기록 대상에 따라 다르기 때문에 Environment Record를 구분한다.
- Declarative Environment Record(선언적 환경 레코드)
- function, 변수, catch 문에서 사용한다.
- 앞 절에서 환경 레코드에 설정한다고 했는데 실제로 DER에 설정된다.
- Object Environment Record(오브젝트 환경 레코드)
- 글로벌 함수와 변수, with 문에서 사용
- 정적이 아니라 동적이기 때문
- Global Environment
EC:{ GE: { ER: { OER: Global Object }, OLER: null } }
- Global Object에서 사용
- Lexical Environment Component와 형태가 같음
- 동적으로 함수와 변수 바인딩
- 함수에서 var 키워드를 사용하지 않고 변수를 선언하면 글로벌 오브젝트에 설정되기 때문
- 이런 이유로 오브젝트 환경 레코드 사용
- 외부 렉시컬 환경 참조 값은 null
- This Binding Component
- 목적
- this로, 함수를 호출한 오브젝트의 프로퍼티에 액세스한다.
- 예) this.propertyName
- Access Mechanism
- obj.book() 형태에서 this로 obj를 참조할 수 있도록 This Binding Component에 obj 참조를 설정
- obj의 프로퍼티가 변경되면 동적으로 참조 (설정이 아닌 참조)
- 실행 과정
var obj = {point: 100}; obj.getPoint = function() { return this.point; } console.log(obj.getPoint()); // 100
- 마지막 줄에서 getPoint() 함수 호출
- Execution Context 생성
- 3개의 Component 생성( LEC, VEC, TBC )
- This Binding Component에 getPoint()에서 this로 obj의 프로퍼티를 사용할 수 있도록 바인딩
- 초기화 단계. 파라미터, 함수 선언문, 변수 선언이 없다.
- 실행 단계 1. return this.point 실행
- 실행 단계 2. TBC에서 point 검색 - getPoint() 함수를 호출한 오브젝트가 TBC에 참조된 상태
- 실행 단계 3. TBC에 point 프로퍼티가 있으므로 100을 반환
- 추가 설명: obj.getPoint()에서 obj의 프로퍼티가 TBC에 바인딩되도록 의도적으로 설계해야 한다.
EC: { LEC= { ER: { DER: {}, OER: {}}, OLER: {}, VEC: {}, TBC: { point: 100, getPoint: function(){} } }
- 목적
- 호출 스택 (Call Stack)
- Execution Context의 논리적 구조
- FILO 순서
- 함수가 호출되면 스택의 가장 위에 EC가 위치하게 된다.
- 다시 함수 안에서 함수를 호출하면 호출된 함수의 EC가 스택의 가장 위에 놓이게 된다.
- 함수가 종료되면 스택에서 빠져 나옴(FILO)
- 가장 아래는 Global Object의 함수가 위치한다.
function one() { two(); console.log(1); } function two() { three(); console.log(2); } function three() { console.log(3); } one();
- 3 2 1 순으로 출력된다.
- 파라미터 매핑
- 함수 호출
- 함수가 호출되면 3개의 파라미터 값을 EC로 넘겨 줍니다.
- 함수를 호출한 오브젝트
- 함수 코드
- 호출한 함수의 파라미터 값
- 함수를 호출한 오브젝트를 TBC에 설정하여 this로 참조
- 함수 코드
- function object의 [[Code]]에 설정되어 있음
- 호출한 함수의 파라미터 값
- 호출된 함수의 Argument object에 설정
- 함수가 호출되면 3개의 파라미터 값을 EC로 넘겨 줍니다.
- 파라미터 값 매핑
- 호출한 함수에서 넘겨 준 파라미터 값을 호출된 함수의 파라미터 작성 순서에 맞추어 값을 매핑하는 것
- 엔진 처리 관점
- EC로 넘겨준 파라미터 값과 function object의 [[FormalParameters]]에 작성된 이름에 값을 매핑하고 결과를 DEC에 설정하는 것
- 함수 호출
- 파라미터 이름에 값 매핑 방법
var obj = {}; obj.getTotal = function(one, two) { return one + two; } console.log(obj.getTotal(11, 22, 77));
- getTotal 오브젝트의 [[FormalParameters]]에서 호출된 함수의 파라미터 이름을 구합니다. 파라미터 이름은 [“one”, “two”] 형태로 [[FormalParameters]]는 function object를 생성할 때 설정한다.
- 파라미터 이름 배열을 하나씩 읽는다.
- 파라미터에서 index 번째의 값을 구한다. 인덱스에 값이 없으면 undefined 반환 (오류가 발생하는 것은 아니다.)
- 파라미터 이름의 이름과 값을 DER에 {one: 11, two: 22} 형태로 설정한다. 같은 이름이 있으면 값이 대체된다.
- 파라미터 이름을 전부 읽을 때까지 배열을 읽고, 값을 구하는 작업을 반복한다.
- 여기서 77(값)은 매핑되는 파라미터 이름이 없어 DER에 저장되지는 않지만 함수에 Argument Object에는 들어가게 된다.
** 출처1. 인프런 강좌_자바스크립트 중고급: 근본 핵심 이해
** 출처2. https://velog.io/@imacoolgirlyo/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-Hoisting-The-Execution-Context-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-6bjsmmlmgy