<aside> 💡 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다. 이는 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념이다. 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.
위와 같은 현상 때문에 다른 언어에서 발견할 수 없는 특이한 현상이 발생한다.
</aside>
스택 vs 큐
콜스택
콜스택
에 쌓아 올린다. 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다.
구성 방법 (여러가지가 있지만 여기서 사실 함수만 생각하면 된다.
)
실행 컨텍스트의 구성 예시
// ---- 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);
(1번) 전역 컨텍스트가 콜스택에 담긴다.
(3번) outer 함수를 호출하면 outer에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성 > 콜스택에 담는다.
(2번) inner함수의 실행 컨텍스트를 생성 > 콜스택에 담긴다.
코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
실행 컨텍스트 객체의 실체 (= 담기는 정보)
var a = 3
의 경우 var a
를 의미두가지에 담기는 내용은 완벽하게 동일하다. 차이는 스냅샷 유지 여부이다.
결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용한다.
구성 요소 VE, LE 모두 같다
environementRecord
, outerEnvironmentReference
로 구성되어 있다.현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 기록된다 라는 의미로 이해해보면 record
와 일맥상통.
수집 대상 정보
컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집
<aside> 💡 순서대로 수집을 할 뿐, 코드가 실행되지 않는다.
</aside>
끌어올리다는 의미를 가진다.
변수정보 수집을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드는 실행 전의 상태이다. (JS 엔진은 코드 실행 전 이미 모든 변수 정보를 알고 있는 것.)
변수 정보 수집 과정을 이해하기 쉽게 설명한 가상 개념
<aside> 💡 가상개념이라는 말은, 실제로는 그렇지 않더라도 사람이 이해하기 쉬운 말로 풀어 표현했다는 것을 의미한다.
</aside>
매개변수 및 변수는 선언부를 호이스팅한다.
<적용 전>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기
function a () {
var x = 1;
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
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);
console.log(x);
x = 2;
console.log(x);
}
a(1);
함수 선언은 전체를 호이스팅한다.
<적용 전>
//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();
<aside> 💡 - 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
</aside>
함수 정의의 3가지 방식
// 함수 선언문. 함수명 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(); // 에러!
함수 정의에 따른 호이스팅 차이
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이 일어나고, 이를 정리해보면 아래와 같다.
// 함수 선언문은 전체를 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;
};
함수 선언문을 주의해야 하는 이유
아래 코드를 보면 함수 선언문은 전체가 hosting작업이 이루어지기 때문에 sum이라는 함수 전체가 바뀌는 현상이 생긴다.
...
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);
스코프
스코프 체인
식별자의 유효 범위를 안에서부터 바깥으로 차례로 검색해나가는 것.
outerEnvironmentReference(이하 outer)
지금까지 LE의 구성요소인 record와 outer중 record에 대해 알아봤다면 이번에는 outer에 대해서 알아보자.
outer
스코프 체인이 가능하게 하는 것 (외부 환경의 참조 정보)
선언될 당시
의 LexicalEnvironment를 참조한다.A함수 내부에 B함수 선언 → B함수 내부에 C함수 선언(Linked List)
한 경우뭔소린지 모르겠고 코드를 한번 보자!
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // undefined
var a = 3;
};
inner();
console.log(a); // 3
};
outer();
console.log(a); // 3