실행 컨텍스트는 생성된다
실행 컨텍스트 스택
어떤 코드인가에 따라 어떠한 룰로 실행 컨텍스트가 생성된다.
A룰이면 고스톱룰 B룰이면 포커룰
이룰은 3가지다
- 전역 코드 : 전역 영역에 존재하는 코드
- Eval 코드 : eval 함수로 실행되는 코드
- 함수 코드 : 함수 내에 존재하는 코드
일반적으로 자주 사용되는 룰은 1,3이다
룰이 전역코드인 경우의 실행 컨텍스트의 행동
스코프 체인의 생성과 초기화 Variable Instantiation(변수 객체화) 실행 this value 결정
룰이 함수코드일 경우의 실행컨텍스트의 행동
- 스코프 체인의 생성과 초기화, 2. Variable Instantiation 실행, 3. this value 결정이 순차적으로 실행된다.
단, 전역 코드와 다른 점은 이번 실행되는 코드는 함수 코드라는 것이다. 따라서 1. 스코프 체인의 생성과 초기화, 2. Variable Instantiation 실행, 3. this value 결정은 전역 코드의 룰이 아닌 함수 코드의 룰이 적용된다.
ss
전역 코드로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다
ss
실행 컨텍스트(Execution Context)는 scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리이다.
실행 컨텍스트를 바로 이해하지 못하면 코드 독해가 어려워지며 디버깅도 매우 곤란해 질 것이다.
ECMAScript 스펙에 따르면 실행 컨텍스트를 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라고 정의한다. 좀 더 쉽게 말하자면 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경 이라고 말할 수 있겠다. 여기서 말하는 실행 가능한 코드는 아래와 같다.
전역 코드 : 전역 영역에 존재하는 코드 Eval 코드 : eval 함수로 실행되는 코드 함수 코드 : 함수 내에 존재하는 코드 일반적으로 실행 가능한 코드는 전역 코드와 함수 내 코드이다.
자바스크립트 엔진은 코드를 실행하기 위하여 실행에 필요한 여러가지 정보를 알고 있어야 한다. 실행에 필요한 여러가지 정보란 아래와 같은 것들이 있다.
변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티 함수 선언 변수의 유효범위(Scope) this 이와 같이 실행에 필요한 정보를 형상화하고 구분하기 위해 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체의 형태로 관리한다. 아래의 코드를 살펴보자.
실행 컨텍스트의 3가지 프로퍼티
실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이지만 물리적으로는 객체의 형태를 가지며 아래의 3가지 프로퍼티를 소유한다.
- VO
- Scope Chain
- thisValue
1. Variable Object (VO / 변수객체)
실행 컨텍스트가 생성되면 자바스크립트 엔진은 실행에 필요한 여러 정보들을 담을 객체를 생성한다. 이를 Variable Object(VO / 변수 객체)라고 한다. Variable Object는 코드가 실행될 때 엔진에 의해 참조되며 코드에서는 접근할 수 없다.
Variable Object는 아래의 정보를 담는 객체이다.
변수 매개변수(parameter)와 인수 정보(arguments) 함수 선언(함수 표현식은 제외) Variable Object는 실행 컨텍스트의 프로퍼티이기 때문에 값을 갖는데 이 값은 다른 객체를 가리킨다. 그런데 전역 코드 실행시 생성되는 전역 컨텍스트의 경우와 함수를 실행할 때 생성되는 함수 컨텍스트의 경우, 가리키는 객체가 다르다. 이는 전역 코드와 함수의 내용이 다르기 때문이다. 예를 들어 전역 코드에는 매개변수가 없지만 함수에는 매개변수가 있다.
Variable Object가 가리키는 객체는 아래와 같다.
전역 컨텍스트의 경우 Variable Object는 유일하며 최상위에 위치하고 모든 전역 변수, 전역 함수 등을 포함하는 전역 객체(Global Object / GO)를 가리킨다. 전역 객체는 전역에 선언된 전역 변수와 전역 함수를 프로퍼티로 소유한다.
22
실행 컨텍스트는 자바스크립트가 왜 그렇게 동작하는 지 여러분께 설명해줍니다. 개념이 상당히 복잡하기 때문에 입문자분들도 이해하기 쉽도록 최대한 쉽게 알려드리기 위해 노력하겠습니다. 따라서 몇 과정은 실제 엔진 동작보다 간소하게 설명해드릴 겁니다. 예를 들면 활성 객체 같은 것은 여기서 설명하지 않겠습니다.
컨텍스트는 한국말로 번역하면 문맥입니다. 쉽게 코드의 실행 환경이라고 이해하시면 될 거 같습니다. 어려워도 포기하지 않고 끝까지 읽으시면 이해가 될 겁니다. 포기하지 마세요! 차분히 순서를 따라가시면 됩니다.
var name = ‘zero’; // (1)변수 선언 (6)변수 대입 function wow(word) { // (2)변수 선언 (3)변수 대입 console.log(word + ‘ ‘ + name); // (11) } function say () { // (4)변수 선언 (5)변수 대입 var name = ‘nero’; // (8) console.log(name); // (9) wow(‘hello’); // (10) } say(); // (7) 먼저 어떻게 console이 찍힐지 생각해보세요. 주석 괄호 안의 숫자 순으로 실행됩니다. 전 시간 lexical scoping을 기억하신다면 결과가 nero, hello zero라는 걸 아실 겁니다.(답을 못 맞췄거나 lexcial scoping을 모르신다면 꼭 이전 강좌 를 보고 오세요) 이것이 어떻게 실행되는지 내부를 살펴보죠.
일단 처음 코드를 실행(여기서 실행은 브라우저가 스크립트를 로딩해서 실행하는 걸 말합니다)하는 순간 모든 것을 포함하는 전역 컨텍스트가 생깁니다. 모든 것을 관리하는 환경입니다. 페이지가 종료될 때까지 유지됩니다. 전역 컨텍스트 말고도 함수 컨텍스트가 있는데요. 전 시간에 자바스크립트는 함수 스코프를 따른다고 했죠? 함수를 호출할 때마다 함수 컨텍스트가 하나씩 더 생깁니다. 컨텍스트의 원칙 네 가지를 알려드리겠습니다.
먼저 전역 컨텍스트 하나 생성 후, 함수 호출 시마다 컨텍스트가 생깁니다. 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생성됩니다. 컨텍스트 생성 후 함수가 실행되는데, 사용되는 변수들은 변수 객체 안에서 값을 찾고, 없다면 스코프 체인을 따라 올라가며 찾습니다. 함수 실행이 마무리되면 해당 컨텍스트는 사라집니다.(클로저 제외) 페이지가 종료되면 전역 컨텍스트가 사라집니다. 이제 아까 코드를 이 원칙들에 따라서 실행해보겠습니다.
전역 컨텍스트
전역 컨텍스트가 생성된 후 두 번째 원칙에 따라 변수객체, scope chain, this가 들어옵니다. 전역 컨텍스트는 arguments(함수의 인자를 말합니다)가 없고요. variable은 해당 스코프의 변수들입니다. name, wow, say가 있네요.
scope chain(스코프 체인, 자신과 상위 스코프들의 변수객체입니다)은 자기 자신인 전역 변수객체입니다. this는 따로 설정되어 있지 않으면 window입니다. this를 바꾸는 방법이 바로 new를 호출하는 겁니다. (또는 함수에 다른 this 값을 bind할 수도 있습니다) 일반 함수의 this가 왜 window인지 아시겠죠? 원래 기본적으로 window고 new나 bind같은 상황에서 this가 바뀌는 겁니다.
이걸 객체 형식으로 표현해보겠습니다.
‘전역 컨텍스트’: { 변수객체: { arguments: null, variable: [‘name’, ‘wow’, ‘say’], }, scopeChain: [‘전역 변수객체’], this: window, } 이제 코드를 위에서부터 실행하는데요. wow랑 say는 호이스팅 때문에 선언과 동시에 대입이 됩니다. 호이스팅은 나중에 설명드리니 잠시 기다려주세요. 그 후 variable의 name에 ‘zero’가 대입됩니다.
variable: [{ name: ‘zero’ }, { wow: Function }, { say: Function }] 함수 컨텍스트 그 후 (7)번에서 say();를 하는 순간 새로운 컨텍스트인 say 함수 컨텍스트가 생깁니다. 아까 전역 컨텍스트는 그대로 있고요. arguments는 없고, variable은 name뿐이네요. scope chain은 say 변수객체와 상위의 전역 변수객체입니다. this는 따로 설정해준 적이 없으니까 window입니다.
'say 컨텍스트': {
변수객체: {
arguments: null,
variable: ['name'], // 초기화 후 [{ name: 'nero' }]가 됨
},
scopeChain: ['say 변수객체', '전역 변수객체'],
this: window,
}
say를 호출한 후 위에서부터 차례대로((8)~(10) 실행하는데요. variable의 name에 nero를 대입해주고 나서 console.log(name);이 있습니다. name 변수는 say 컨텍스트 안에서 찾으면 됩니다. variable에 name이 nero라고 되어 있네요. name이 콘솔에 찍힙니다. 그 다음엔 wow(‘hello’)가 있는데요. say 컨텍스트 안에서 wow 변수를 찾을 수 없습니다. 찾을 수 없다면 scope chain을 따라 올라가 상위 변수객체에서 찾습니다. 그래서 전역 변수객체에서 찾습니다. 전역 변수객체의 variable에 wow라는 함수가 있네요. 이걸 호출합니다.
(10)번에서 wow함수가 호출되었으니 wow 컨텍스트도 생기겠죠? arguments는 word = ‘hello’고, scope chain은 wow 스코프와 전역 스코프입니다. 여기서 중요한 게 lexical scoping에 따라 wow 함수의 스코프 체인은 선언 시에 이미 정해져 있습니다. 따라서 say 스코프는 wow 컨텍스트의 scope chain이 아닙니다. variable은 없고, this는 window입니다.
'wow 컨텍스트': {
변수객체: {
arguments: [{ word : 'hello' }],
variable: null,
},
scopeChain: ['wow 변수객체', '전역 변수객체'],
this: window,
}
이제 컨텍스트가 생긴 후 함수가 실행 되는데요. 나중에 생긴 wow가 가장 먼저 실행됩니다. wow 함수 안에서 console.log(word + ‘ ‘ + name);이 있는데요. word랑 name 변수는 wow 컨텍스트에서 찾으시면 됩니다. word는 arguments에서 찾을 수 있고, name은 wow 변수객체에는 값이 없으니, scope chain을 따라 전역 스코프에서 찾으시면 됩니다. 전역 변수객체로 올라가니 variable에 name이 zero라고 되어 있네요. 그래서 hello zero가 되는 겁니다. hello nero가 아니라요. wow 컨텍스트에 따르면 wow 함수는 애초에 say 컨텍스트와 일절 관련이 없었던 겁니다.
이제 wow 함수 종료 후 wow 컨텍스트가 사라지고, say 함수의 실행이 마무리됩니다. 따라서 say 컨텍스트도 사라지고, 마지막에 전역 컨텍스트도 사라집니다. 함수 실행, 변수 선언 등 모든 게 다 논리적입니다. 그래서 컨텍스트 개념을 이해하면 자바스크립트의 모든 문제들을 풀 수 있습니다. 추가적으로 이벤트 루프까지 아신다면 완벽합니다. 이벤트 루프 강좌도 꼭 봐주세요.