02. 실행 컨텍스트
// 전역 컨텍스트 -- (1) 번으로 실행
var a =1;
function outer(){
function inner(){
..
}
inner(); --- (3) 번으로 실행
}
outer() ---- (2)번으로 실행.
Stack에는 1 → 실행 -> 2 → 실행 -> 3 이 쌓이게 되고 3 -> 2 -> 1 순으로 stack에서 빠진다.
컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는데 필요한 환경 정보
들을 수집해서 실행 컨텍스트 객체에 저장한다.
VariableEnvironment
: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경 사항은 반영되지 않음.LexicalEnvironment
: 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨.ThisBinding
: this 식별자가 바라봐야 할 대상 객체.environmentRecord
에 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 함수 자체, var로 선언된 변수의 식별자.
environmentRecord
는 현재 실행될 컨텍스트의 코드 내에서 어떤 식별자가 있는지만 관심이 있고 어떤 값이 할당될 것에는 관심이 없다.
순서대로 수집한다.
코드가 실행되기 전에 자바스크립트 엔진은 이미 환경에 속한 코드의 변수명을 모두 알고 있다. ⇒ 이는 호이스팅을 할 수 있게 해줌.
자바스크립트 엔진이 식별자들을 최상단으로 끌어올려 놓는다.
function a (x) { // 수집 1
console.log(x); -- 1
var x; // 수집 2
console.log(x) -- 2
var x=2 // 수집 3
console.log(x) -- 3
}
a(1)
우리가 예상하기로는 1
,undefined
,2
가 출력될 것이라고 예상하지만 호이스팅이 일어나게 되어 1
,1
,2
라는 결과가 나온다.
// 실제가 아닌 엔진 변환 예시
function a (x) {
var x; // 수집 1
var x; // 수집 2
var x; // 수집 3
x=1;
console.log(x); // 1
console.log(x) // 1
x=2
console.log(x) // 2
}
함수가 있으면 함수도 hosting 된다.!
// 호이스팅 적용 전
function a() {
console.log(b); // (1)
var b = 'bbb'; // 수집 대상 1 (변수 선언)
console.log(b); // (2)
function b() { // 수집 대상 2 (함수 선언)
console.log(b); // (3)
}
}
a();
위의 예는 어떨까?
(1)은 undefined
, (2)는 bbb
, (3)은 b
함수가 출력될 것 같다.
// 호이스팅이 적용됐다고 가정.
function a() {
var b;
var b = function b () { } // 바뀐 부분
console.log(b); // (1) 함수 b
b = 'bbb';
console.log(b); // (2) bbb
}
a();
함수의 할당 부분이 최상단으로
끌어올려진 부분이다. 이와 같이 함수 선언은 전체를 최상단으로 끌어올린다.
function a () { } // 함수 선언문. 함수명 a가 곧 변수명
const b = function () {} // (익명)함수 표현식 변수 b가 곧 함수명
함수 선언방식은 순서와 상관없이 함수 전체가 hosting 되기 때문에 함수 호출보다 늦게 선언되어도 문제가 없다.
단, 나중에 같은 이름으로 함수가 선언될 경우 나중에 선언된 함수가 첫번째 함수를 덮어 씌우기 때문에 문제가 발생한다. (더더욱 에러도 발생하지 않음... ⇒ 매우 위험)
따라서, 함수 표현식으로 선언한 방법이 안전하다. 단, 호출을 하기 전에 반드시 선언을 해줘야 한다. !!!
전역 스코프 (Global scope)
코드 어디에서든지 참조할 수 있다.
지역 스코프 (Local scope or Function-level scope)
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
지역 스코프는 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다.
단, ES6에서는 블록 레벨 스코프를 사용 가능하다. ES5 var는 적용이 안되고 const, let, class, strict mode에서만 범위가 구분된다.
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0
식별자의 유효범위를 안에서부터 바깥으로 차례로 검색하는 것. (outerEnvironmentReference에 의해 가능해진다.)
여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하다.
outer 함수 내부에서는 outer 및 전역 변수만 접근가능하고 inner 스코프 내부에서 생성된 변수에는 접근하지 못한다.
inner 함수 내부에서는 inner, outer ,전역 스코프 모두 접근 가능하다.
// 스코프 체인 예제
var a = 1;
var outer = function () {
var inner = function () {
console.log(a);
var a = 3;
};
inner();
console.log(a);
};
outer();
console.log(a);
스코프 체인이란 식별자가 현재 컨텍스트에 없다면 outerEnvironmentReference 를 통해 외부의 컨텍스트에 있는지 알아보며, 있다면 해당 식별자를 사용할 수 있도록 동작을 한다.
References
위키북스 코어자바스크립트 도서, 정재남 지음