실행할 코드에 제공할 환경 정보들을 모아놓은 객체로, 자바 스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념입니다. 자바스크립트는 실행 컨텍스트 객체가 활성화되는 시점에 선언된 변수를 호이스팅하고, 외부 환경 정보를 구성하고, this값을 설정하는 등의
동작을 수행하게 됩니다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택에 쌓아올렸다가 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 방식으로 전체 코드의 환경과 순서를 보장합니다. 우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행하는 것입니다.
보다 나은 이해를 위해 코드로 설명하겠습니다.
// --------------------------(1)
var a = 1;
function outer() {
function inner() {
console.log(a) // undefined
var a = 3;
}
inner(); // ---------------(2)
console.log(a) //1
}
outer(); // -----------------(3)
console.log(a); //1
위의 코드는 다음과 같은 순서로 실행되게 됩니다.
이렇게 실행 컨텍스트가 활성화되면 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드를 실행하는데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장합니다.이 객체에 담기는 정보들은 다음과 같습니다.
VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 & 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경 사항은 반영되지 않음
LexicalEnvironment : 처음에는 VariableEnvrionment와 동일하나 변경 사항이 실시간으로 반영
ThisBinding : this 식별자가 바라봐야 할 대상 객체
VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같으나, 최초 실행 시의 스냅샷을 유지한다는 점에서 다릅니다. 실행 컨텍스트를 생성할 때 VariableEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 됩니다. 내부는 동일하게 environmentRecord와 outer-EnvironmentReference로 구성되어 있습니다.
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)
위의 코드에 hoisting 개념을 더해보면 이렇게 변환할 수 있겠습니다(그러나 자바스크립트 엔진이 실제로 변경하는 것은 아닙니다! 이해를 위한 예시일 뿐입니다.)
매개변수를 변수 선언/할당과 같다고 간주해서 변환한 상태
function a (x) {
var x = 1; //수집 대상 1(매개변수 선언)
console.log(x);
var x; //수집 대상 2(변수 선언)
console.log(x);
var x = 2; //수집 대상 3(변수 선언)
console.log(x)
}
a();
호이스팅을 마친 상태
function a(x) {
var x; //수집대상 1의 변수 선언 부분
var x; //수집대상 2의 변수 선언 부분
var x; //수집대상 3의 변수 선언 부분
x = 1; //수집대상 1의 할당 부분
console.log(x) //(1)
console.log(x) //(2)
x = 2; //수집 대상 3의 할당 부분
console.log(x) //(3)
}
a();
따라서 결과값은 1, 1, 2
로 나오게 됩니다.
두 표현 모두 함수를 새롭게 정의할 때 쓰이는 방식인데, 그 중 함수 선언문은 함수의 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미하고, 함수 표현식은 정의한 함수를 별도의 변수에 할당하는 것을 의미합니다. 함수 선언문과 함수 표현식의 가장 큰 차이는 Hoisting에 있습니다. 이 역시도 코드를 통해 살펴보죠.
원본 코드
console.log(sum(1,2));
console.log(multiply(3,4));
function sum (a,b) { //함수 선언문 sum
return a + b;
}
var multiply = function (a,b) { //함수 표현식 multiply
return a * b;
}
호이스팅을 마친 상태
var sum = function sum (a,b) { //함수 선언문은 함수 전체를 Hoisting합니다.
return a + b;
}
var multiply; //변수는 선언부만 끌어올립니다.
console.log(sum(1,2));
console.log(multiply(3,4));
multiply = function (a,b) { //변수의 할당부는 원래 자리에 남겨둡니다.
return a * b;
}
multiply에는 값이 할당돼있지 않기 때문에 multiply is not a function
이라는 에러 메세지가 출력되고 변수의 할당부는 실행되지 않은 채 런타임이 종료됩니다.
이런 이유로 대규모 프로젝트에서 함수를 사용하거나, 협업을 진행하게 될 때, 비교적 안전한 함수 표현식을 사용하는 것이 안전합니다. 전역 컨텍스트가 활성화될 때 전역공간에 선언된 함수들이 모두 가장 위로 끌어올려지기 때문에 만약 동일한 변수명에 서로 다른 값을 할당할 경우 에러가 나타나기 때문입니다.
스코프는 식별자에 대한 유효범위를 뜻합니다. ES5까지의 자바스크립트는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성됩니다.(ES6에서 블록 스코프가 새롭게 추가되었습니다. let, const, class, arrow function등이 있습니다.)
식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것을 스코프 체인이라고 합니다. 그리고 이를 가능하게 하는 것이 outerEnvironmentReference 입니다.
이 역시도 코드를 통해 살펴보겠습니다.
var a = 1;
var outer = function () {
var inner = function () {
console.log(a);
var a = 3;
}
inner();
console.log(a)
}
outer();
console.log(a)
{ inner }
식별자를 지정합니다. outerEnvironmentReference에는 outer 함수가 선언될 당시의 LexicalEnvironment가 담기게 됩니다. outer 함수의 경우 전역 공간에서 선언됐으므로 전역 컨텍스트의 LexicalEnvironment를 참조복사합니다. 이를 [GLOBAL, { a, outer }]
라고 표기합니다. 첫 번째는 실행 컨텍스트의 이름, 두 번째는 environmentRecord 객체입니다.(this: 전역 객체){ a }
식별자를 지정합니다. outerEnvironmentReference에는 inner 함수가 선언될 당시의 LexicalEnvironment가 담기게 됩니다. inner 함수의 경우 outer함수 내부에서 선언됐으므로 outer 함수의 LexicalEnvironment인 [ outer, { inner }]
를 참조복사합니다.(this: 전역 객체)undefined
를 출력하게 됩니다.1
을 출력합니다.이처럼 함수 내부에서 특정 변수에 접근하고자 할 때, 먼저 environmentRecord부터 검색하고 존재하지 않을 경우 outerEnvironmentReference로 넘어가게 됩니다. 이를 변수 은닉화라고 합니다.
위의 글은 온전히 학습 목적을 위해 작성한 글입니다. 위 내용의 모든 지적 재산권,저작권은 코어 자바스크립트 저자에게 있으며, 무단 전재 및 재배포를 금합니다.