실행 컨텍스트란?
- ECMAScript 스펙에 따르면 실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이다. 즉, 코드가 실행되기 위해 필요한 환경을 말한다.
- 하나의 컨텍스트는 하나의 동작을 실행하는 환경으로 이와 관련 없는 함수나 변수 등의 코드를 만나게 되면 새로운 컨텍스트를 생성한다. 그리고 이 컨텍스트들은 한번에 하나의 동작만 처리하는 자바스크립트의 콜백 스택에 쌓이게 된다.
- 자바스크립트 엔진의 동작 방식은 다음과 같다.
- 실행 가능한 코드 (잔역 코드, 함수 내 코드 등)를 만나면 실행 컨텍스트 스택이 생성된다.
- 전역 코드가 스택에 들어오면 전역 실행 컨텍스트가 생성되어 스택에 쌓인다. 전역 실행 컨텍스트는 해당 애플리케이션이 종료될때까지 유지된다.
- 함수가 호출되면 이 함수를 위한 실행 컨텍스트가 생성되어 스택에 쌓이고 함수가 값을 반환하거나 종료되면 해당 함수의 실행 컨텍스트가 소멸되고 스택에서 pop 하게 되고 스택에서 바로 직전의 실행 컨텍스트에 제어권이 돌아간다.
- 실행 제어권은 항상 스택의 top 최상위에 있는 실행 컨텍스트가 갖는다.
- 실행 가능한 코드 (잔역 코드, 함수 내 코드 등)를 만나면 실행 컨텍스트 스택이 생성된다.
실행 컨텍스트 객체의 프로퍼티
실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념, 즉, 어떤 코드의 동작 한 덩어리이다. 이는 물리적으로 객체의 형태를 가지며 다음과 같은 프로퍼티를 갖는다.
변수 객체 Variable Object
- 실행 컨텍스트가 생성되면 자바스크립트 엔진은 실행에 필요한 정보들을 담을 객체인 변수 객체를 생성한다. 그 변수 객체에 코드에서 선언된 변수, 함수에 전달된 인자, 함수 선언 (function()...) 을 담아 엔진이 코드를 실행할 때 참조한다.
- 그런데 이때 실행 컨텍스트가 전역 컨텍스트인 경우와 함수 컨텍스트인 경우 변수 객체가 가리키는 객체가 다르다.
- 전역 컨텍스트일 경우, 변수 객체는 유일하며 최상위에 위히하고 모든 전역 변수, 전역 함수등을 포함하는 전역 객체 (Global object)를 가리킨다. 전역 객체는 글로벌로 선언된 변수와 함수를 프로퍼티로 갖는다. (변수 객체 = 전역 객체)
- 함수 컨텍스트일 경우, 변수 객체는 활성 객체 (Activation object)를 가리키며 인자들의 정보를 배열로 담고 있는 객체인 argument object가 추가된다. (변수 객체 = 활성 객체 + 인자 배열 객체)
스코프 체인 Scope chain
- 간단히 말하자면 변수 객체에 담긴 정보들의 리스트를 말한다.
- 전역 객체 또는 활성 객체들이 담겨있다.
- 실행 컨텍스트의 활성 객체를 선두로 순차적으로 스택의 상위에 있는 실행 컨텍스트의 활성 객체를 가리키며 마지막으로 전역 객체를 가리킨다. 즉, 실행 컨텍스트 스택의 top->bottom순서!
- 엔진은 스코프 체인을 통해 렉시컬 스코프를 파악한다.
- 중첩 함수의 경우 부모 함수의 스코프 체인이 자식 함우의 스코프 체인에 포함된다.
- 함수 실행 중 변수를 만나면 그 변수를 현재 스코프에서 검색하는데 그 변수를 찾을 때가지 스코프 체인을 순서대로 검색한다. 만약 끝까지 스코프 체인에서 변수를 찾아내지 못하면 정의되지 않은 변수에 접근하는 것으로 판단하여 Reference 에러가 발생한다.
- 함수의 Scope 프로퍼티로 조회할 수 있다.
this value
- this에 할당되는 값은 함수 호출 패턴에 의해 결정된다
- https://seungyooon.tistory.com/261
실행 컨텍스트의 생성 과정
- 전역 객체가 생성된다.
- Global Object = 전역 컨텍스트의 VO가 가리키는 객체
- 전역 객체는 코드 어떠한 곳에서도 접근할 수 있다.
- 생성된 직후 초기 상태에는 DOM, BOM, 빌트인 객체등이 설정되어 있다.
- 코드를 만나면 실행 컨텍스트가 생성되고 이것이 실행 컨텍스트 스택에 쌓이고 생성된 실행 컨텍스트를 바탕으로 다음과 같은 처리가 이루어진다.
- 스코프 체인 생성 및 초기화 : 전역 객체의 레퍼런스를 포함하는 리스트
- 변수 객체화 : 객체 변수에 프로퍼티와 값을 추가하는 것
- 전역 실행 컨텍스트의 경우 객체 변수가 전역 객체를 가리킨다.
- 변수 객체화 실행 순서 (반드시 이 순서대로 실행됨)
1. 함수 실행 컨텍스트인 경우 변수 객체가 매개변수(키/프로퍼티) - 인자(밸류)를 가진다.
2. 함수 표현식이 아닌 함수 선언 자체의 함수명이 변수 객체의 프로퍼티가 되고 생성된 함수 객체가 값으로 설정된다. (=함수 호이스팅)
3. 코드 내 변수가 변수 객체의 프로퍼티가 되고 undefined가 값이 된다 (변수 호이스팅)
변수 객체 = { parameter : argument, function_name : function() object, variable_name : undefined }
- 함수 선언 처리
- 전역 실행 컨텍스트의 변수 객체가 가리키는 전역 객체는 함수들의 이름을 프로퍼티로 가지고 있고, 각 함수 이름 프로퍼티들은 함수객체들을 값으로 가진다.
- 이때 함수 이름 프로퍼티의 값으로 설정된 함수 객체들은 스코프 프로퍼티를 갖는다. 이 스코프 프로퍼티는 햄수 객체가 실행되는 환경으로 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체를 값으로 설정한다. 즉, 현재 함수 실행 컨텍스트의 스코프 체인이 전역 객체 내 함수 프로퍼티의 값인 함수 객체의 스코프 프로퍼티 값과 일치하는 것!
- 이 내부 함수의 스코프 프로퍼티는 자신의 실행환경 (=렉시컬 환경) + 외부 함수의 실행환경 + 전역 객체를 가지는데 클로저 현상으로 인해 외부 함수 실행 컨텍스트가 소멸하여도 내부 함수 스코프의 프로퍼티가 가리키는 외부 함수의 실행환경, 활성 객체는 소멸하지 않고 계속 참조할 수 있다.
- 이렇게 스코프 체인이 가리키는 변수 객체에 이미 함수이름을 프로퍼티로, 해당 함수 객체를 그 값으로 가지고 있기 때문에 함수 선언 코드가 아직 실행되지 않아도 그 이전에 이 함수를 호출할 수 있다 = 함수 호이스팅
- 변수 선언 처리
- 변수들은 변수 객체화를 하면서 변수 명을 변수객체의 프로퍼티 이름으로, undefined를 값으로 설정되는데 그 과정을 좀 더 세분화 해보면 다음과 같다.
1. 선언 declaration : 변수 객체에 변수를 등록하여 스코프 참조 대상이 된다.
2. 초기화 initialization : 변수 객체에 등록된 변수를 메모리에 할당하면서 그 값을 undefined로 초기화한다.
3. 할당 assignment : undefined로 초기화된 변수에 실제 코드에서 할당된 값을 할당한다. - var 키워드로 선언된 변수는 선언과 초기화가 한번에 일어나 변수 선언 코드를 만나기 이전에도 해당 변수가 변수 객체에 존재하기 때문에 접근할 수 있다. 다만 이것은 undefined로 반환된다 ( = 변수 호이스팅) 그러다 var 변수에 값을 할당하는 코드를 만나면 값의 할당 (3단계)이 이루어진다.
- 변수들은 변수 객체화를 하면서 변수 명을 변수객체의 프로퍼티 이름으로, undefined를 값으로 설정되는데 그 과정을 좀 더 세분화 해보면 다음과 같다.
- this value 결정
- this value는 전역 컨텍스트에서 전역 객체를 가리키고 있다가 함수 호출 패턴에 의해 this 에 값이 할당된다.
- 전역 코드 실행
- 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체 변수 리스트(=전역 객체들)를 검색하면서 해당 변수명 프로퍼티를 찾는다. 발견하면 값을 할당하고 없으면 레퍼런스 에러를 발생시킨다.
- 함수 실행 코드를 만나면 새로운 함수 실행 컨텍스트가 생성된다. 그럼 위와 동일하게 함수 컨텍스트에 대한 스코프 체인을 생성 및 초기화하고 변수 객체화를 하고, this value를 결정한다.
1. 함수의 스코프 체인 생성과 초기화 : 활성 객체에 대한 레퍼런스를 스코프 체인의 선두에 둔다. 활성 객체들은 파라미터를 프로퍼티 이름으로 설정한다. 그리고 부모가 되는 컨텍스트가 참조하는 스코프 체인도 이 함수 스코프 체인에 push된다.
2. 스코프 체인에 들어가 있는 활성 객체들의 프로퍼티들에 실제 값을 할당한다.
3. 함수 호출 패턴에 의해 this 값을 결정한다.
- 함수 코드 실행
- 함수 내 변수 값 할당 : 현재 함수 실행 컨텍스트의 스코프 체인이 참조하고 있는 변구 객체 (리스트)를 앞에서부터 검색하며 변수명에 해당하는 프로퍼티를 발견하면 거기에 값을 할당한다.
- 함수 내 함수 실행 : 새로운 함수 실행 컨텍스트를 생성하고 스택에 쌓는다. 함수 실행 컨텍스트의 실행 과정과 동일한 단계들이 순차적으로 진행된다.
References
'프로그래밍 언어 > Javascript' 카테고리의 다른 글
Javascript) Input에서 엔터누르면 이벤트 트리거 (0) | 2021.12.08 |
---|---|
Javascript) http에서 클립보드 쓰기 기능 사용하기 (0) | 2021.12.01 |
Javascript) Event Loop 비동기 콜백, 자바스크립트 동작 원리 (0) | 2021.11.30 |
Javascript) Web Storage (0) | 2021.11.29 |
Javascript) this 용법 (0) | 2021.11.29 |