개요
실행컨텍스트는 코드가 실행되는 환경을 말합니다.
크게 전역 컨텍스트와 함수 컨텍스트가 있는데,
우선 코드가 실행되면 (브라우저가 스크립트를 로딩해서 실행할 때)
모든 것을 포함하고 관리하는 전역 컨텍스트가 생깁니다.
페이지가 종료 될때까지 유지됩니다. 다음 함수 컨텍스트는 함수 호출시마다 생성됩니다.
이 실행컨텍스트들은 생성될때마다 변수객체, 스코프체인, this가 생성됩니다.
컨텍스트 생성 후 함수가 실행되는데 사용되는 변수들은 변수 객체 안에서 값을 찾고
없으면 스코프 체인을 따라 올라가며 찾습니다.
함수 실행이 마무리되면 해당 컨텍스트는 사라집니다. (클로저 제외)
페이지가 종료되면 전역 컨텍스트가 사라집니다.
- 먼저 전역 컨텍스트 하나 생성 후, 함수 호출 시마다 컨텍스트가 생깁니다.
- 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생성됩니다.
- 컨텍스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 값을 찾고, 없다면 스코프 체인을 따라 올라가며 찾습니다.
- 함수 실행이 마무리되면 해당 컨텍스트는 사라집니다.(클로저 제외) 페이지가 종료되면 전역 컨텍스트가 사라집니다.
들어가기 전
실행 컨텍스트는 scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리다.
실행 컨텍스트를 바로 이해하지 못하면 코드 독해가 어려워지고, 디버깅도 곤란해집니다.
실행 컨텍스트란? (Execution Context)
EC란 실행 가능한 자바스크립트 코드 블록이 실행되는 환경을 말한다.
- 인사이드 자바스크립트 140p
실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이자,
실행 가능한 코드가 실행되기 위해 필요한 환경이다.
EC 의 3가지 종류
1. Global Execution Context (전역 실행 컨텍스트)
2. Functional Execution Context (함수 실행 컨텍스트)
3. Eval Function Execution Context (eval 실행 컨텍스트)
전역 실행 컨텍스트
가장 먼저 콜스택에 올라가는 EC이다.전역 EC는 다른 EC들과 달리 arguments 객체가 없고,
전역 객체 하나만을 포함하는 스코프 체인과 this가 있다.전역 실행 컨텍스트는 <script /> 태그를 마주치면 생성된다.
함수 실행 컨텍스트
함수가 호출될 때마다 생성되는 실행컨텍스트로 arguments와 스코프 체인, this가 있다.
Eval 실행 컨텍스트
eval() 함수를 실행해서 만들어진 것을 말한다.
자바스크립트 엔진은 코드를 실행하기 위해 아래와 같은 여러 정보가 필요하다.
- 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
- 함수 선언
- 변수의 유효범위 (scope)
- this
실행 컨텍스트 스택
코드 실행시 실행 컨텍스트 스택(Stack)이 생성하고 소멸한다.
자바스크리브 엔진은 실행에 필요한 정보를 형상화하고 구분하기 위해,
실행 컨텍스트를 물리적 객체의 형태로 관리한다.
const x = 'xxx';
function foo () {
const y = 'yyy';
function bar () {
const z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
현재 실행 중인 컨텍스트에서 이 컨텍스트와 관련없는 코드(예를 들면 다른 함수)가 실행되면
새로운 컨텍스트가 생성된다. 이 컨텍스트는 스택에 쌓이는 동시에 컨트롤을 넘겨 받는다.
*EC : Execution context
1) 실행 컨텍스트 스택 생성
컨트롤이 실행 가능한 코드로 이동하면 논리적 스택 구조를 가지는 새로운 실행 컨텍스트 스택이 생성된다.
스택은 후입선출(Last In First Out)의 나열 구조이다.
2) 전역 실행 컨텍스트 생성 (global EC)
전역 코드 (global EC)로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다.
전역 실행 컨텍스트는 애플리케이션이 종료될 때(웹페이지에서 나가거나 브라우저를 닫을 때)까지 유지된다.
3) 함수 실행 컨텍스트 생성 ( 1.함수 호출, 2. eval()들이 실행되면 실행컨텍스트 생성함)
함수를 호출하면 해당 함수의 실행 컨텍스트가 생성되며 직전에 실행된 코드 블록의 실행 컨텍스트 위에 쌓인다.
4) 함수 실행 컨텍스트 파기
함수 실행이 끝나면 해당 함수의 실행 컨텍스트를 파기하고 직전의 실행 컨텍스트에 컨트롤을 반환한다.
실행 컨텍스트의 3가지 객체
실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적 개념이지만,
물리적으로는 아래의 3가지 객체의 형태를 가진다.
실행 컨텍스트의 구조
Execution context | |
Variable object | {vars, function declarations, arguments...} |
Scope chain | [Variable object + all parent scopes ] |
thisValue | Context object |
Variable Object (변수객체)
변수객체는 실행 컨텍스트의 프로퍼티이다.
실행 컨텍스트가 생성되면 자바스크립트 엔진은 실행에 필요한 여러 정보들을 담을 객체를 생성한다.
이를 변수 객체라고 한다. 변수 객체는 코드가 실행될 때 엔진에 의해 참조되며 코드에서는 접근 할 수 없다.
변수 객체가 담는 정보들
- 변수
- 매개변수(parameter)와 인수 정보(arguments)
- 함수 선언 (함수 표현식은 제외)
변수객체는 실행 컨텍스트의 프로퍼티이기 때문에 값을 가지고 있는데, 이 값은 다른 객체를 가리킨다.
변수 객체가 가리키는 객체들
1) 전역 컨텍스트(global EC)의 경우 -> GO(전역객체)를 가리킨다.
전역객체(Global Object)는 최상위에 위치한 모든 전역 함수, 전역 변수 등을 프로퍼티로 소유한다.
2) 함수 컨텍스트의 경우 -> AO(활성객체)를 가리킨다.
함수가 호출되면 함수 컨텍스트에 만들어지고,
함수 컨텍스트에는 AO라는 특수 객체가 생성됩니다.
AO는 함수 컨텍스트의 VO로 사용됩니다.
함수 컨텍스트의 VO는 위에 전역 컨텍스트의 VO와 동일하지만,
변수와 함수 이외에도 arguments object가 추가된다는 차이점이 있습니다.
arguments object는 매개변수(parameter)와 전달인자(argument)로 이루어져있습니다.
3) Scope Chain (SC) (이해가 아직 안감) 자세한 내용->tyle.io/blog/54
스코프 체인은 컨텍스트의 코드들을 검색하는 객체의 목록입니다.
해당 실행 컨텍스트에 a라는 변수를 선언했다면, 이 변수를 찾을 때 스코프 체인에서 검색하는 것입니다.
만약 현재 스코프 체인에서 검색에 실패했을 경우, 상위 컨텍스트에서 검색하고 이 과정을 반복하여
전역 컨텍스트까지 검색을 하게됩니다. 전역 컨텍스트까지 검색을 실패했다면 자바스크립트엔진은 오류를 보내는 것입니다.
스코프체인(scope Chain)은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장한다.
즉, 스코프 체인은 해당 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 전역 객체 또는
활성객체의 리스트를 가리킨다.
현재 실행 컨텍스트의 활성 객체(AO)를 선두로 하여 순차적으로 상위 컨텍스트의 활성 객체(AO)를 가리키며 마지막 리스트는 전역 객체를 가리킨다.
스코프 체인은 식별자 중에서 객체(전역 객체 제외)의 프로퍼티가 아닌 식별자, 즉 변수를 검색하는 메커니즘이다.
식별자 중에서 변수가 아닌 객체의 프로퍼티(물론 메소드도 포함된다)를 검색하는 메커니즘은 프로토타입 체인(Prototype Chain)이다.
엔진은 스코프 체인을 통해 렉시컬 스코프를 파악한다. 함수가 중첩 상태일 때 하위함수 내에서 상위함수의 스코프와 전역 스코프까지 참조할 수 있는데 이것는 스코프 체인을 검색을 통해 가능하다. 함수가 중첩되어 있으면 중첩될 때마다 부모 함수의 Scope가 자식 함수의 스코프 체인에 포함된다. 함수 실행중에 변수를 만나면 그 변수를 우선 현재 Scope, 즉 Activation Object에서 검색해보고, 만약 검색에 실패하면 스코프 체인에 담겨진 순서대로 그 검색을 이어가게 되는 것이다. 이것이 스코프 체인이라고 불리는 이유이다.
예를 들어 함수 내의 코드에서 변수를 참조하면 엔진은 스코프 체인의 첫번째 리스트가 가리키는 AO에 접근하여 변수를 검색한다. 만일 검색에 실패하면 다음 리스트가 가리키는 Activation Object(또는 전역 객체)를 검색한다. 이와 같이 순차적으로 스코프 체인에서 변수를 검색하는데 결국 검색에 실패하면 정의되지 않은 변수에 접근하는 것으로 판단하여 Reference 에러를 발생시킨다. 스코프 체인은 함수의 감추인 프로퍼티인 [[Scope]]로 참조할 수 있다.
This value
this는, 함수의 호출 방식에 따라 그 값이 달라집니다.
함수가 객체를 참조해서 생성되었다면 this는 해당 객체를 가리키게 됩니다.
이외 경우 보통 this는 글로벌 객체를 가르킵니다.
실행 컨텍스트의 생성 과정
예시 코드를 통해 생성 과정을 살펴보자.
const x = 'xxx';
function foo () {
const y = 'yyy';
function bar () {
const z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
1) 전역 코드에 진입 - 전역객체 생성
컨트롤이 실행 컨텍스트에 진입하기 이전에 유일한 전역 객체(GO)가 생성된다.
전역 객체는 단일 사본으로 존재하며 이 객체의 프로퍼티는 코드의 어떠한 곳에도 접근할 수 있다.
초기 상태의 전역 객체에는 빌트인 객체(Math, String, Array, BOM, DOM)가 설정되어있다.
2) 전역 실행 컨텍스트가 생성
전역 객체가 생성된 후, 전역 코드로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다.
3) 이 실행컨텍스트를 바탕으로 이하의 처리가 실행된다.
1. 스코프체인의 생성 초기화
2. Variable Instantiation(변수 객체화) 실행
3. this value 결정
3) 스코프 체인의 생성과 초기화
실행 컨텍스트가 생성된 이후 가장 먼저 스코프 체인의 생성과 초기화가 실행된다.
이 때, 스코프 체인은 전역 객체의 레퍼런스를 포함하는 리스트가 된다.
4) 변수 객체화 (Variable Instantiation) 실행
스코프 체인의 생성과 초기화가 종료하면 변수 객체화가 실행된다.
변수 객체화란 Variable Object에 프로퍼티와 값을 추가하는 것을 의미한다.
즉, 변수, 매개변수와 인수 정보(arguments), 함수 선언을 Variable Object에 추가하여 객체화하는 과정을 말한다.
전역 코드의 경우, Variable Object는 Global Object를 가리킨다.
변수 객체화 실행 순서
1. 매개변수(parameter)가 Variable Object의 프로퍼티로, 인수(arguments)가 값으로 설정된다. (함수 코드인경우)
2. 대상 코드 내의 함수 선언(함수 표현식 제외)을 대상으로 함수명이 Variable Object의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다.(함수 호이스팅)
3. 대상 코드 내의 변수 선언을 대상으로 변수명이 Variable Object의 프로퍼티로, undefined가 값으로 설정된다.(변수 호이스팅)
매개변수, 인수 차이점
function sayHello(a, b) { // a, b는 매개변수(parameter)
return a + b
}
sayHello(1, 2); // 1, 2는 인수 (argument)
위 예제 코드를 보면 변수 x와 함수 foo()가 선언되었다.
일단, 함수 코드가 아니라 전역코드이기 때문에 1번이 생략된다.
그래서 2. 함수 foo의 선언이 처리되고, 3. 변수 x의 선언이 처리된다.
5. 함수 foo의 선언 처리 (복습 필요)
함수 선언은 Variable Instantiation 실행순서 2.와 같이 선언된 함수명 foo가
Variable Object의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다.
(전역코드인 경우 Global Object의 프로퍼티로)
생성된 함수 객체는 [[scope]] 프로퍼티를 가지게 된다.
[[scope]] 프로퍼티는 함수 객체만이 소유하는 내부 프로퍼티로서 함수 객체가 실행되는 환경을 가리킨다.
따라서 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체를 값으로 설정한다.
내부 함수의 [[scopes]] 프로퍼티는 자신의 실행 환경(Lexical Enviroment)과 자신을 포함하는
외부 함수의 실행환경과 전역 객체를 가리키는데 이때 자신을 포함하는 외부 함수의 실행 컨텍스트가 소멸하여도
[[scope]] 프로퍼티가 가리키는 외부 함수의 실행 환경(Activation object)은 소멸하지 않고 참조할 수 있다. 이것이 클로저다.
지금까지 살펴본 실행 컨텍스트는 아직 코드가 실행되기 이전이다. 하지만 스코프 체인이 가리키는 변수 객체(VO)에 이미 함수가 등록되어 있으므로 이후 코드를 실행할 때 함수선언식 이전에 함수를 호출할 수 있게 되었다.
이때 알 수 있는 것은 함수선언식의 경우, 변수 객체(VO)에 함수표현식과 동일하게 함수명을 프로퍼티로 함수 객체를 할당한다는 것이다. 단, 함수선언식은 변수 객체(VO)에 함수명을 프로퍼티로 추가하고 즉시 함수 객체를 즉시 할당하지만 함수 표현식은 일반 변수의 방식을 따른다. 따라서 함수선언식의 경우, 선언문 이전에 함수를 호출할 수 있다. 이러한 현상을 함수 호이스팅(Function Hoisting)이라 한다.
6. 변수 x의 선언 처리
(내용 추가 필요)
poiemaweb.com/js-execution-context
실행 컨텍스트의 실행
실행 컨텍스트의 실행 패턴 알아볼게요.
var a = 'A'
function foo() {
var b = 'B'
function bar() {
var c = 'C'
console.log(a, b, c)
}
bar()
}
foo()
// 실행 결과 : A B C
1. 변수 a의 할당
변수 선언 3단계
1) 선언 단계 -> 변수 객체에 변수를 등록
2) 초기화 단계 -> 등록된 변수를 메모리에 할당
3) 할당 단계 -> 초기화된 변수에 실제값을 할당
변수는 선언과 동시에 초기화 됩니다.
선언된 변수명은 변수 객체에 등록되고,
등록된 변수는 실행 컨텍스트에서 스코프 체인을 통해 참조될 수 있습니다.
만약 변수 할당 전에 변수를 참조하면,
3) 할당 단계를 거치지 않았기 때문에
변수 명은 undefined이 됩니다.
즉 할당 되지않고 변수객체에 등록된 값은 undefined입니다.
이 개념을 호이스팅이라고 합니다.
이렇게 변수 a는 앞선 과정을 거쳐 전역 객체에 할당됩니다.
2. 함수 foo의 할당
함수 선언이 변수 객체에 등록되는 과정은
foo 이름이 프로퍼티명이 되고, 선언된 함수 객체가 값으로 설정됩니다.
함수 객체는 [[Scopes]]라는 프로퍼티를 가지게 됩니다.
이 프로퍼티는 함수 객체가 실행되는 환경을 저장합니다.
즉 선언될 때의 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체를 값으로 저장합니다.
만약 선언될 때의 실행 컨텍스트가 소멸 되더라도, [[Scopes]]에 자신과 외부 함수의 실행 환경을 저장하고 있으므로, 외부 컨텍스트의 소멸에 상관없이 실행할 수 있게 되는 것 입니다. 그리고 이러한 개념을 클로저라고 합니다.
3. 함수 foo의 실행
전역 코드에 있는 foo()가 실행되면전역 코드에 있는 foo()가 실행되면 새로운 실행 컨텍스트가 생성되고, 그 컨텍스트는 컨텍스트 스택에 쌓이게 됩니다. 그러면 원래 활성화되어 있던 전역 컨텍스트는 비활성화가 되고, 방금 생성되어 스택에 쌓인 컨텍스트가 활성화 됩니다.
이 컨텍스트가 활성화가 되면 전역 컨텍스트와 같이, 1. 변수 a의 할당과 같이 변수 b의 할당이 이루어집니다.
이때 전역 객체와 달라지는 것은 활성화 객체를 사용한다는 점일 것입니다. 활성화 객체는 매개변수와 전달인자를 저장하기 때문에 먼저 arguments(매개변수) 프로퍼티의 초기화를 실행합니다.
그 후에는 해당 컨텍스트의 스코프 체인이 참조하고 있는 객체(전역 컨텍스트)를 스코프 체인에 추가시킵니다. 만약 전역 컨텍스트가 아닌, 함수 컨텍스트라면, 전역 컨텍스트가 추가될 때 까지 이 작업을 반복하여 스코프 체인을 채웁니다.
이전 작업들과 다른 것은 활성화 객체를 사용한다는 점뿐입니다.
4. thisValue 결정
선언 처리가 모두 끝났다면 this value가 결정됩니다.
앞서 말한 것 처럼 this value는 함수의 호출 방식에 따라 값이 결정됩니다.
foo함수의 경우 this value는 전역 객체가 됩니다.
5. 변수 b의 할당
변수 b는, 선언이 되기 전에 해당 컨텍스트의 스코프 체인에서 b라는 이름의 프로퍼티가 있는지 검색하고 만약 없다면,
선언, 초기화 단계를 거치며 활성화 객체에 할당됩니다.
6. 함수 bar의 할당
함수 bar또한, 변수 b처럼 스코프 체인에서 검색을 하고, 활성화 객체에 할당되어 집니다.
7. 함수 bar의 실행
함수 bar가 실행되면, 마찬가지로 새로운 실행 컨텍스트가 스택에 쌓이고 활성화됩니다.
해당 실행 컨텍스트의 스코프 체인은 bar의 활성화 객체와, 전역 객체의 변수 객체를 가지게 됩니다.
8. 변수 c할당
5번과 동일
jong-hui.github.io/devlog/2019/11/13/execution-context/
www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0
'프론트엔드 > Javascript' 카테고리의 다른 글
화살표 함수는 언제, 왜 써야할까? (0) | 2020.10.30 |
---|---|
JavaScript- require vs import (CommonJs와 ES6) 차이점 (0) | 2020.10.30 |
이벤트 버블링, 캡쳐링, 위임 (0) | 2020.10.21 |
자바스크립트의 동시성을 지원하는 이벤트루프 (0) | 2020.10.21 |
비동기 처리하는 3가지 방법 (0) | 2020.10.19 |