실행 컨텍스트 객체는 다음 세가지의 정보를 가지고 있다.
- VariableEnvironment
- LexicalEnvironment
- thisBinding
1. VariableEnvironment
a. 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
ex) var a = 3
의 경우 var a
를 의미
b. 외부 환경 정보(=outer)를 갖고 있다.
c. 선언 시점 LexicalEnvironment의 snapshot
2. LexicalEnvironment
a. VE와 동일하지만, 변경사항을 실시간으로 반영한다.
3. thisBinding
a. this 식별자가 바라봐야할 객체
둘 다 ‘environmentRecord’와 ‘outerEnvironmentReference’로 구성되어 있다. 그리고 environmentRecord가 곧 'record', outerEnvironmentReference가 곧 'outer'라고 할 수 있을 것이다.
결국 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용.
LE에 environmentRecord는
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장(수집 및
기록
)된다. (record
)- 수집 대상이 되는 정보는
함수에 지정된 매개변수 식별자
,함수 자체
,var로 선언된 변수 식별자
등이다.(기록되는건 식별자이지 변수가 아니다.)- 컨텍스트 내부를 처음부터 끝까지
순서대로
훑어가며 수집한다.(수집
하는거지,실행
되는 것이 아니다.)
<적용 전ex>
<script>
function a (x) {
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
</script>
<매개변수 적용ex>
<script>
function a () {
var x = 1;
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
</script>
언뜻 보면, 결과값이 각각 1, undefined, 2로 나올 것 같지만 실제로는 1, 1, 2라는 결과가 나온다. 호이스팅 과정을 거치면 JS엔진은 다음과 같은 순서로 읽기 때문이다.
<호이스팅 적용ex>
<script>
function a () {
var x;
var x;
var x;
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
</script>
<적용전ex>
<script>
function a () {
console.log(b);
var b = 'bbb';
console.log(b);
function b() { }
console.log(b);
}
a();
</script>
결과는 undefined, 'bbb', function일 것 같지만 사실은 그렇지 않다. 다음과 같은 호이스팅 결과를 기반으로 읽기 때문이다.
<호이스팅 적용ex>
<script>
function a () {
var b; // 변수 선언부 호이스팅
function b() { } // 함수 선언은 전체를 호이스팅
console.log(b);
b = 'bbb'; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
</script>
그래서 결과적으로 function, 'bbb', 'bbb'라는 결과가 도출되게 된다.
함수를 정의하는데는 세가지 방식이 있다.
<script>
// 함수 선언문. 함수명 a가 곧 변수명
// function 정의부만 존재, 할당 명령이 없는 경우
function a () { /* ... */ }
a(); // 실행 ok
// 함수 표현식. 정의한 function을 별도 변수에 할당하는 경우
// (1) 익명함수표현식 : 변수명 b가 곧 변수명(일반적 case에요)
var b = function () { /* ... */ }
b(); // 실행 ok
// (2) 기명 함수 표현식 : 변수명은 c, 함수명은 d
// d()는 c() 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문
var c = function d () { /* ... */ }
c(); // 실행 ok
d(); // 에러!
</script>
이런 차이 때문에 다음과 같은 문제가 발생할 수 있다.
<script>
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;
}
</script>
라는 함수들을 호이스팅하는 과정에 있어서 다음과 같이 호이스팅된다.
<script>
// 함수 선언문은 전체를 hoisting
function sum (a, b) { // 함수 선언문 sum
return a + b;
}
// 변수는 선언부만 hoisting
var multiply;
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function (a, b) { // 변수의 할당부는 원래 자리
return a + b;
};
</script>
console.log(sum(1, 2));
는 정상작동하게 되지만, console.log(multiply(3, 4));
는 정상적으로 작동하지 않게 된다.