자바스크립트의 실행 컨텍스트
는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.
실행 컨텍스트를 이해하기 위해서는 Call stack
을 이해해야 한다.
실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체라고 한다.
동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위해서 설명한 '스텍'의 한 종류인 콜스텍
이 쌓아 올린다. 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다.
a. 구성방법
- 전역공간
- eval() 함수
- 함수
b. 실행컨텍스트 구성 예시 코드
// ---- 1번
var a = 1;
function outer() {
function inner() {
console.log(a); //undefined
var a = 3;
}
inner(); // ---- 2번
console.log(a);
}
outer(); // ---- 3번
console.log(a);
c. 실행컨텍스트 구성 순서
위 코드는 아래 순서로 진행된다.
코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
d. 결국 특정 실행 컨텍스트가 생성되듯 시점이 콜 스택의 맨 위에 쌓이는(노출되는) 순간을 의미한다, 곧, 현재 실행할 코드에 해당 실행 컨텍스트가 관여하게 되는 시점을 의미한다고 받아드릴 수 있다.
1. VariableEnvironment
현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
- var a = 3;
- 위의 경우 var a를 의미
외부 환경 정보(=outer)를 갖고 있다.
선언 시점 LexicalEnvironment의 snapshot
2. LexicalEnvironment
- VariableEnvironment와 동일하지만, 변경사항을 실시간으로 반영한다.
3. ThisBinding
- this 식별자가 바라봐야 할 객체
VE vs LE
이 두가지는 담기는 항목은 완벽하게 동일하다, 그러나 스냅샷 유지여부는 다음과 같이 다르다
- VE: 스냅샷을 유지한다.
- LE: 스냅샷을 유지하지 않는다, 즉 실시간으로 변경사항을 계속해서 반영한다.
=> 결국 실행 컨텍스트를 생성할 때 VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 사용한다.
구성 요소 (VE, LE 서로 같다)
- VE, LE 모두 동일하며,
environmentRecord
와outerEnvironmentReference
로 구성- environmentRecord(=record)
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
- 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등
- outerEnvironmentReference(=outer)
식별자 정보
들이 저장된다, 기록된다
고 이해하고 보면 record
라는 말과 일맥상통한다.순서대로 수집한다고 했지, 코드가 실행된다고 하지는 않음!
가상개념이라는 말은 실제로는 그렇진 않더라도 사람이 이해하기 쉬운 말로 풀어 표현했다는 것을 의미한다.
1. 호이스팅 법칙1 : 매개변수 및 변수는 선언부를 호이스팅 한다.
다음 주석에 달려있는 3가지 action point에 따라 실습을 진행한다.
적용 전
/// 적용 전 // action point 1: 매개변수 다시 쓰기(js엔진은 똑같이 이해한다) // action point 2: 결과 예상하기 // action point 3: 호이스팅 적용해본 후 결과를 다시 예상해보기 function a (x) { console.log(x); // 1 var x; console.log(x); // 1 var x = 2; console.log(x); // 2 } a(1);
매개변수 적용
// 매개변수 적용 //action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다) //action point 2 : 결과 예상하기 //action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기 function a () { var x = 1; console.log(x); // 1 var x; console.log(x); // 1 var x = 2; console.log(x); // 2 } a(1);
호이스팅 적용
// 호이스팅 적용 //action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다) //action point 2 : 결과 예상하기 //action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기 function a () { var x; var x; var x; x = 1; console.log(x); // 1 console.log(x); // 1 x = 2; console.log(x); // 2 } a(1);
2. 호이스팅 규칙2 : 함수 선언은 전체를 호이스팅합니다.
적용 전
//action point 1 : 결과 값 예상해보기 //action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기 function a () { console.log(b); var b = 'bbb'; console.log(b); function b() { } console.log(b); } a();
호이스팅 적용
//action point 1 : 결과 값 예상해보기 //action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기 function a () { var b; // 변수 선언부 호이스팅 function b() { } // 함수 선언은 전체를 호이스팅 console.log(b); b = 'bbb'; // 변수의 할당부는 원래 자리에 console.log(b); console.log(b); } a();
함수선언문을 함수 표현식으로
//action point 1 : 결과 값 예상해보기 //action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기 function a () { var b; // 변수 선언부 호이스팅 var b = function b() { } // 함수 선언은 전체를 호이스팅 console.log(b); // function b b = 'bbb'; // 변수의 할당부는 원래 자리에 console.log(b); // bbb console.log(b); // bbb } a();
// 함수 선언문. 함수명 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(); // 에러!
실행 컨텍스트
는실행할 코드에 제공할 환경 정보
들을 모아놓은 객체이다.
- 그 객체 안에는 3가지가 존재한다.
✓ VariableEnvironment
✓ LexicalEnvironment
✓ ThisBindings
- VE와 LE는 실행컨텍스트 생성 시점에 내용이 완전히 같고, 이후 스냅샷 유지 여부가 다르다.
- LE는 다음 2가지 정보를 가지고 있다.
✓ record(=environmentRecord) ← 이 record의 수집과정이 hoisting
✓ outer(=outerEnvironmentReference)
함수 선언문과 표현식을 배웠으니, 실질적인 차이를 예시를 통하면
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;
}
LE는 record와 outer를 수집한다, 그 중 record를 수집하는 과정에서 호이스팅이 일어나고, 우리가 알고 있는대로 위로 쭉 끌어올려본 결과를 다시 써보면 아래와 같다.
// 함수 선언문은 전체를 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;
};
함수 선언문과 표현식은 호이스팅 과정에서 극명한 차이를 보인다.
...
console.log(sum(3, 4));
// 함수 선언문으로 짠 코드
// 100번째 줄 : 시니어 개발자 코드(활용하는 곳 -> 200군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
return x + y;
}
...
...
var a = sum(1, 2);
...
// 함수 선언문으로 짠 코드
// 5000번째 줄 : 신입이 개발자 코드(활용하는 곳 -> 10군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
return x + ' + ' + y + ' = ' + (x + y);
}
...
var c = sum(1, 2);
console.log(c);
...
console.log(sum(3, 4));
// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
return x + y;
}
...
...
var a = sum(1, 2);
...
// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
return x + ' + ' + y + ' = ' + (x + y);
}
...
var c = sum(1, 2);
console.log(c);
협업을 많이 하고 복잡한 코드 일수록, 전역 공간에서 이루어지는 코드 협업 일수록
함수 표현식
을 활용하는 습관을 들이도록 하자.