제로초님의 실행 컨텍스트를 바탕으로 정리하였습니다.
(실행) 컨텍스트란? 쉽게 말해 자바스크립트 코드가 실행되는 환경이라고 할 수 있다.
컨텍스트는 크게 두 가지로 나뉜다. 하나는 전역 컨텍스트, 다른 하나는 함수 컨텍스트이다.
처음 브라우저가 스크립트를 로딩하여 실행하는 순간 전역 컨텍스트가 만들어지고, 이는 페이지가 종료될 때까지 유지된다.
이후 함수가 호출될 때마다 함수 컨텍스트가 하나씩 추가로 생기게 되고, 함수 실행이 끝나면 사라지게 된다.
먼저 전역 컨텍스트 하나 생성 후, 함수 호출 시마다 새 함수 컨텍스트가 생긴다.
컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생성된다.
컨텍스트 생성 후 함수가 실행된다. → 사용되는 변수들은 변수객체 안에서 값을 찾고, 없다면 스코프 체인을 따라 올라가며 찾는다.
함수 실행이 마무리되면 해당 함수 컨텍스트는 사라진다.(클로저 제외) 페이지가 종료되면 전역 컨텍스트도 사라진다.
참고 | ECMAScript에서는 실행 컨텍스트의 생성을 다음과 같이 설명한다.
" 현재 실행되는 컨텍스트에서 이 컨텍스트와 관련 없는 실행 코드가 실행되면, 새로운 컨텍스트가 생성되어 스텍에 들어가고 제어권이 그 컨텍스트로 이동한다. "
아래 코드를 바탕으로 위의 네 가지 원칙을 따라가보자.
예제 코드
const name='zero'; //(1)변수 선언 (6)변수 대입
function wow(word){ //(2)변수 선언 (3)변수 대입
console.log(word+' '+name); //(11)
}
function say(){ //(4)변수 선언 (5)변수 대입
const name='nero'; //(8)
console.log(name); //(9)
wow('hello'); //(10)
}
say(); //(7)
먼저, 브라우저가 처음 코드를 로드하고 실행하는 순간 모든 것을 포함하는 전역 컨텍스트가 생긴다.
전역 컨텍스트가 생성되면 변수 객체(arguments, variable), scope chain, this가 들어온다.
이 과정을 좀더 자세히 정리하자면 다음과 같다.
활성 객체와 변수 객체를 다르게 부르는데 사실 활성 객체를 변수 객체로 사용함으로 둘 다 같은 말이라고 한다.
arguments 객체 생성
인자들을 암묵적으로 arguments 객체내부로 넘겨준다.
스코프 체인 생성
변수 생성
this 바인딩
따로 지정한 것이 없으면 전역객체(window)가 할당된다.
코드 실행
이렇게 실행 컨텍스트가 생성되고, 변수 객체가 만들어진 후에 코드에 있는 표현식이 실행된다. (이 때 변수에 값이 할당됨)
전역 컨텍스트의 경우 arguments 객체가 없고 전역 객체 하나만을 포함하는 스코프 체인이 있다. 또한 전역 컨텍스트의 변수 객체는 전역 객체가 된다.
다시 예제로 돌아와 전역 컨텍스트 생성 과정을 살펴보자. (코드 일부 생략)
const name='zero';
function wow(word){
...
}
function say(){
...
}
say();
위 코드를 보면 전역 컨텍스트는,
인 것을 알 수 있다.
이를 객체 형식으로 표현하면 아래와 같다.
'전역 컨텍스트':{
변수객체:{
arguments:null,
variable:['name','wow','say'],
},
scopeChain:['전역 변수객체'],
this: window,
}
이제 코드를 위에서부터 실행한다.
wow랑 say는 함수 선언문으로 생성한 함수이므로 선언과 동시에 function이 할당된다. 그 후 variable의 name에 'zero'가 할당된다.
variable:[{name:'zero'},{wow:Function},{say:Function}]
참고 | 변수와 함수의 초기화
변수
처음 코드를 실행하면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라간다. 이때 변수의 상태는 특수 내부 상태인 uninitialized가 된다.
자바스크립트 엔진은 uninitialized 상태의 변수를 인지하지만, let이나 const를 만나기 전까진 이 변수를 참조할 수 없다.
let의 경우 선언과 할당을 따로 할 수 있기에, 선언부만 만난 경우 프로퍼티 값이 undefined이 되고 (이때부터 변수 사용 가능) 이후 깂이 할당된다.
함수 선언문
일반 변수와 달리 함수 선언문으로 선언한 함수는 바로 초기화된다. 즉 함수 선언문으로 선언된 함수는 렉시컬 환경이 만들어지는 즉시 사용할 수 있다.
선언되기 전에도 함수를 사용할 수 있었던 이유가 바로 이것이다. (함수 표현식은 해당X)
이렇게까지 하면 전역 컨텍스트는 다 만들어졌다. 이제 코드를 실행하다가 함수 호출을 만나면 그때마다 새로운 함수 컨텍스트를 만들게 된다.
say();
를 하는 순간 새로운 컨텍스트인 say 함수 컨텍스트가 추가로 생긴다. (전역 컨텍스트 그대로 있음)
위에서 했던 과정을 그대로 따져보자.
...
function say(){
const name='nero';
console.log(name);
wow('hello');
}
say();
arguments는 없고 variable은 name 뿐.
scope chain은 say 변수 객체와 상위의 전역 변수 객체이다.
this는 따로 설정해준 적 없으므로 window.
'say 컨텍스트':{
변수객체:{
arguments:null,
variable:['name'],// 초기화 후 [{name:'nero'}]가 됨
},
scopeChain:['say 변수 객체','전역 변수 객체'],
this:window,
}
say를 호출한 후 위에서부터 차례대로 코드를 실행한다.
const name='nero';
console.log(name);
wow('hello');
variable의 name에 nero를 대입해주고 나서 console.log(name);
이 있다. name 변수는 say 컨텍스트 안에서 찾으면 된다. variable에 name이 nero라고 되어 있으니 콘솔에 name이 찍힌다.
그 다음 wow('hello');
가 있는데 say 컨텍스트 안에서 wow 변수를 찾을 수 없으므로, scope chain을 따라 올라가 상위 변수객체에서 찾는다. 따라서 전역 변수객체에서 찾아보면 variable에 wow라는 함수가 있으므로 그걸 호출한다.
...
function wow(word){
console.log(word+' '+name);
}
function say(){
...
wow('hello');
}
...
wow 함수가 호출되었으니 wow 컨텍스트도 생기게 된다.
argument는 word='hello'
고, scope chain은 wow 스코프와 전역 스코프이다.
여기서 주의해야할 점은 lexical scoping에 따라 wow 함수의 스코프체인은 선언 시에 이미 정해져 있다는 것이다. 따라서 say 스코프는 wow 컨텍스트의 scope chain이 아니다.
(wow가 say안에서 호출되었다고 헷갈려하면 안된다. 언제 어디서 호출됐는지랑 상관없이 처음 선언되었을 때 스코프 체인이 결정된다.)
다음 variable은 없고, this는 window이다.
`wow 컨텍스트`:{
변수객체:{
arguments:[{word:'hello'}],
variable:null,
},
scopeChain:['wow 변수객체','전역 변수객체'],
this:window,
}
이제 컨텍스트가 생겼으므로 해당 함수가 실행된다.
console.log(word+' '+name);
위 코드를 만나면, word랑 name 변수를 wow 컨텍스트에서 찾는다. word는 arguments에서 찾을 수 있고, name은 wow 변수 객체에 없으니 scope chain을 따라 전역 스코프에서 찾으면 된다. 전역 변수객체의 variable에 name이 zero라고 되어있다.
콘솔에 'hello zero'가 찍힌다.
이제 wow 함수가 종료되고 wow 컨텍스트가 사라진다. say 함수의 실행도 마무리 되어, say 컨텍스트도 사라지고 마지막으로 전역 컨텍스트도 사라진다.
실행 컨텍스트를 구성하는 컴포넌트 중 하나이다.
식별자와 값을 관리하고, 외부 상위 스코프에 대한 참조를 기록하는데 사용하는 자료구조이다.
환경 레코드와 외부 렉시컬 환경 참조에 대한 정보로 구성되어 있다.
렉시컬 중첩 구조 기반으로 변수, 함수 식별자와 값을 관리하는데 사용된다.
함수를 처음 선언하는 순간, 함수는 자신으로부터 가장 가까운 곳 상위 스코프를 참조하는 것을 말한다.