실행할 코드에 제공할 환경정보들을 모아놓은 객체
우리가 일반적으로 구성할 수 있는 방법으로는, 함수를 실행, 호출하는것 뿐!
1. 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 콜스택에 담겨서 활성화된다.
2. 전역 컨텍스트와 관련된 코드들을 순차적으로 실행하다가,
함수 호출문을 만나면 해당 함수의 실행 컨텍스트가 새로 생기고 콜스택의 맨 위에 담긴다.
따라서, 전역 컨텍스트와 관련된 코드를 중단하고
새로 생긴 함수의 실행 컨텍스트와 관련된 코드를 실행한다.
3. 함수가 종료되면, 콜스택에서 제거하고
함수의 실행 컨텍스트가 생기면서 중단되었던 전역 공간의 코드부터 다시 실행한다.
... 반복
실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간, 실행 컨텍스트가 활성화 되면서
자바스크립트 엔진은 해당 컨텍스트와 관련된 코드를 실행하는데에 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장한다.
실행 컨텍스트 객체에 담기는 정보들은 다음과 같다.
variable environment
lexical environment
variable environment
와lexical environment
는 아래와 같이 구성됨
environmentRecord
: 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집outerEnvironmentReference
: 현재 호출된 함수가 선언될 당시의lexicalEnvironment
를 참조
호이스팅?
environmentRecord에 현재 컨텍스트와 관련된 코드의 식별자 정보들을 처음부터 끝까지 쭉 훑어나가며 순서대로 수집하게 되는데, 이 과정을 마치더라도 아직 코드들은 실행되기 전의 상태이다.
즉 코드가 실행되기 전임에도 불구하고, 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 됨
자바스크립트 엔진은, 식별자들을 최상단으로 끌어올려놓은(hoisting) 다음 실제 코드를 실행하는것처럼 보이게끔 한다!
function a(x){ // 수집대상 1 (매개변수임)
console.log(x);
var x; // 수집대상 2 (변수 선언임)
console.log(x);
var x=2; // 수집대상 3 (변수 선언임)
console.log(x);
}
a(1);
을 실행하면 1,undefined,2
가 차례로 찍힐 것 같지만
실제로는 호이스팅에 의해서, 아래 코드와 같이 동작한다.
(엔진이 실제로 이렇게 변환과정을 거치지는 않지만, 이해를 편하게 하기 위함임)
function a(){
var x; // 수집대상 1의 변수 선언부분
var x; // 수집대상 2의 변수 선언부분
var x; // 수집대상 3의 변수 선언부분
x=1; // 수집대상 1의 할당 부분
console.log(x);
console.log(x);
x=2; // 수집대상 3의 할당 부분
console.log(x);
}
a(1);
변수명만 끌어올리고 할당과정은 원래 자리에 그대로 남겨둠.
1,1,2
가 찍힌다.
function a(){
console.log(b);
var b='bbb';
console.log(b);
function b() {};
console.log(b);
}
a();
호이스팅을 알지 못했다면, undefined, bbb, 함수b
가 차례로 출력될거라고 생각했겠지만
위의 코드가 호이스팅되고나면, 아래 코드처럼 동작할 것이라는것을 알 수 있다.
function a(){
var b; // 변수는 선언부만 끌어올림
function b() {}; // 함수 선언은 전체를 끌어올림
console.log(b);
b='bbb'; // 할당부는 원래 자리에 남겨둠.
console.log(b);
console.log(b);
}
a();
따라서 b함수,bbb,bbb
순서로 로그가 찍히게된다.
함수 선언문
함수 표현식
✅ 둘의 가장 중요한 차이
함수 표현식은 변수의 선언부와 할당부가 분리되어, 변수 선언부만 호이스팅 된다는것..!!
그래서 함수 선언문은 선언 전에 호출해도 함수 전체가 호이스팅되어 문제 없이 실행되지만,
함수 표현식은 선언전에 호출하면 "x is not a function" 이라는 오류가 발생하게됨!!
그래서 상대적으로 함수 표현식이 함수 선언문보다 더 안전하다..!
(발생해야할 오류를 잘 나타내주기때문)
식별자에 대한 유효범위
1 var a =1;
2 var outer=function(){
3 var inner=function(){
4 console.log('inner a',a);
5 var a=3;
6 };
7 inner();
8 console.log('outer a ',a);
9 };
10 outer();
11 console.log('global a',a);
위 코드의 실행결과는 어떻게 될까?
(1). 전역 컨텍스트의 environmentRecord
에 {a,outer}
식별자를 저장
전역 컨텍스트는 선언 시점이 없으므로, 전역 컨텍스트의 outerEnvironmentReference
에는 아무것도 담기지 않음
전역 컨텍스트
environmentRecord
: {a, outer}outerEnvironmentReference
: X(1)~(2). 전역 스코프에 있는 변수 a에 1을, outer에 함수를 할당.
(10). outer함수 호출. outer 실행컨텍스트가 활성화되어 2번째 줄로 이동.
(2). outer 실행 컨텍스트의 environmentRecord
에 {inner}
식별자를 저장
outerEnvironmentReference
에는 outer함수가 선언될 당시 === 전역 컨텍스트의 LexicalEnvironment
를 참조복사한다.
outer 실행 컨텍스트
environmentRecord
: {inner}outerEnvironmentReference
: [Global, {a,outer}](3). 변수 inner에 inner함수를 할당한다.
(7). inner함수가 호출되어 inner 실행 컨텍스트가 활성화되고, outer 컨텍스트는 중단되고 3번째줄로 이동된다.
(3). inner 실행 컨텍스트의 environmentRecord
에 {a} 식별자를 저장.
outerEnvironmentReference
에는 inner 함수가 선언될 당시 === outer함수 내부에서 선언되었으므로 outer 함수의 lexicalEnvironment
를 참조복사한다.
inner 실행 컨텍스트
environmentRecord
: {a}outerEnvironmentReference
: [outer, {inner}](4). 현재 활성화 상태인 inner 컨텍스트의 environmentRecord
에 a를 검색하면, 아직 할당된 값이 없으므로 undefined를 출력한다.
(5). inner 스코프에 있는 변수 a에 3을 할당한다.
(6). inner 함수가 종료되고, 실행 컨텍스트가 콜 스택에서 제거되어 outer 실행 컨텍스트가 다시 활성화되면서 앞서 중단되었던 7번째줄의 다음으로 이동한다.
(8). 현재 활성화 상태인 outer 컨텍스트의 environmentRecord
에 a가 존재하지 않으므로, outerEnvironmentReference
에 있는 environmentRecord
인(전역 lexicalEnvironment
) a에 저장된 값인 1을 반환한다.
(9). outer 함수가 종료되고, 실행 컨텍스트가 콜 스택에서 제거되어 전역 컨텍스트가 다시 활성화되면서, 앞서 중단했던 10번째줄의 다음으로 이동한다.
(11). 현재 활성화 상태인 전역 컨텍스트의 environmentRecord
에서 바로 a를 찾을 수 있으므로 1을 출력한다. 모든 코드의 실행이 완료되고, 전역 컨텍스트가 콜 스택에서 제거되고 종료한다.
출처