코어자바스크립트 02.실행컨텍스트
Summary
- 실행컨텍스트
- 실행컨텍스트 동작
- 실행 컨텍스트의 구성 방법
- (예제 2-1) 실행 컨텍스트와 콜스택
- 콜스택 변화
- 활성화된 실행 컨텍스트의 수집정보
- VariableEnvironment
- LexicalEnvironment
- environmentRecord와 호이스팅(hoisting)
- 2-3 호이스팅 규칙 - 매개 변수와 변수 선언에 대한 호이스팅
- 예제 2-4 변수 선언 부분과 할당 부분을 나눠 호이스팅
- 2-5 호이스팅 규칙 - 함수 선언에 대한 호이스팅
- 2-6 함수 선언의 호이스팅(2) 호이스팅을 마친상태
- 함수 선언문과 함수 표현식
- 함수 선언문의 위험성
- 함수 표현식
- 스코프, 스코프 체인, outerEnvironmentReference
- 스코프체인
- 스코프체인-크롬전용
- 전역변수와 지역변수
실행컨텍스트
- 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
스택(Stack), 큐 (Queue)
Stack
- 나중에 집어넣은 데이터가 먼저 나옵니다. LIFO(Last In First Out)
- 데이터를 집어넣는 push, 데이터를 추출하는 pop
Queue
- 먼저 집어넣은 데이터가 먼저 나옵니다. FIFO(First In First Out)
- 데이터를 집어넣는 enqueue, 데이터를 추출하는 dequeue
실행컨텍스트 동작
- 동일한 환경에 있는 코드들을 실행할때 필요한 환경 정보들을 모아 컨텍스트 구성
- 컨텍스트들을 순서대로 Call Stack에 쌓아올림
- 최상위 컨텍스트와 관련있는 코드들을 실행
- 전체 코드의 환경과 순서 보장
실행 컨텍스트의 구성 방법
- 전역공간 -> 자동실행
- 함수 -> 가장 흔한 실행 컨텍스트 구성방법
- 블록 ->
{ }
으로 둘러 쌓인 코드내부 ES6부터 지원
(예제 2-1) 실행 컨텍스트와 콜스택
1
2
3
4
5
6
7
8
9
10
11
//(1) 전역컨텍스트가 콜스텍에 담긴다
function outer() {
function inner() {
console.log(a); //undefined
var a = 3;
}
inner(); //(2)
console.log("inner", a);
}
outer(); //(3) outer 실행컨텍스트를 생성한 후 콜 스택에 담는다.
console.log("outer", a);}
실행순서: (1) -> (3) -> (2)
콜스택 변화
활성화된 실행 컨텍스트의 수집정보
- VariableEnvironment
- environmentRecord (snapshot)
- outerEnvironmentReference (snapshot)
- LexicalEnvironment
- environmentRecord
- outerEnvironmentReference
- ThisBinding
VariableEnvironment
- 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지
- 실행 컨텍스트 생성 시 VariableEnvironment에 먼저 정보를 담고, 그대로 LexicalEnvironment에 복사 한 후 주로 LexicalEnvironment를 활용
- 내부 구성 요소 : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보가 선언 시점의 스냅샷으로 저장
- environmentRecord (snapshot)
- outerEnvironmentReference (snapshot)
LexicalEnvironment
- 정의
- 한국어로 번역 시 ‘어휘적 환경’, ‘정적 환경’을 사용하는데 모두 적절치 않음
- 현재 컨텍스트 내부에는 a, b, c와 같은 식별자들이 있고, 그 외부 정보는 D를 참조하도록 구성
- 위와 같은 구성이 마치 사전과 비슷하여 ‘사전적 환경’이 더 적합한 표현
- 하지만 그냥 의미만 이해하고 지칭할 때는 LexicalEnvironment로 사용하는 것이 가장 좋음
- 내부 구성 요소 : VariableEnvironment와 동일하지만 변경 사항이 실시간으로 반영 됨
- environmentRecord
- outerEnvironmentReference
environmentRecord와 호이스팅(hoisting)
- environmentRecord
- 현재 컨텍스트와 관련 된 코드의 식별자 정보들이 저장
- 컨텍스트를 구성하는 함수에 지정된
매개변수 식별자
- 선언한 함수가 있을 경우 그
함수 자체
- var로 선언 된
변수의 식별자
- 컨텍스트를 구성하는 함수에 지정된
- 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집
- 전역 실행 컨텍스트
- 변수 객체 생성 대신 자바스크립트 구동 환경이 별도로 제공하는 전역 객체를 활용
- 브라우저의 window, Node.js의 global 객체 등
- 이들은 내장(native) 객체가 아닌 호스트(host) 객체로 분류
- 호이스팅의 개념
- 호이스팅(hoisting) : hoist (끌어 올리다) + ~ing → 식별자들을 최상단으로 끌어 올려 놓자
- 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념
- 자바스크립트 엔진이 실제로 끌어 올리지는 않지만 편의상 끌어 올리는 것으로 간주하자는 것
2-3 호이스팅 규칙 - 매개 변수와 변수 선언에 대한 호이스팅
- environmentRecord는 현재 실행 될 컨텍스트 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있지, 각 식별자들이 어떤 값이 할당 될지에는 관심이 없음
1
2
3
4
5
6
7
8
9
10
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);
예제 2-4 변수 선언 부분과 할당 부분을 나눠 호이스팅
1
2
3
4
5
6
7
8
9
10
11
12
function a() {
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();
- environmentRecord는 현재 실행 될 컨텍스트 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있기 때문에 변수 선언 부분만 최상단으로 올라가게 됨 예제 2-4 결과확인
2-5 호이스팅 규칙 - 함수 선언에 대한 호이스팅
1
2
3
4
5
6
7
8
function a() {
console.log(b); // (1)
var b = "yurihaha"; // 수집 대상 1 (변수 선언)
console.log(b); // (2)
function b() {} // 수집 대상 2 (함수 선언)
console.log(b); // (3)
}
a();
- a 함수를 실행하는 순간 a 함수의 실행컨텍스트 생성 (변수명, 함수선언 정보 위로 끌어올림)
- 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수선언은 함수 전체를 끌어올린다.
- 수집대상 1, 수집대상 2를 순서대로 끌어올린다.
2-6 함수 선언의 호이스팅(2) 호이스팅을 마친상태
1
2
3
4
5
6
7
8
9
10
function a() {
var b; // ------------------- 수집 대상 1(변수 선언) 의 선언 부분
function b() {} // ---------- 수집 대상 2(함수 선언) 의 전체
// var b = function b() {} // (*) 위의 코드를 이렇게 표현도 가능
console.log(b); // ---------- (1)
b = "bbb"; // --------------- 수집 대상 1의 할당 부분
console.log(b); // ---------- (2)
console.log(b); // ---------- (3)
}
a();
함수 선언문과 함수 표현식
- 함수 선언문 : 함수 정의부만 존재하고 별도의 할당 명령이 없는 것
- 호이스팅 시 함수 전체를 끌어 올림
- 함수 표현식 : 정의한 함수를 별도의 변수에 할당하는 것
- 익명 함수 표현식 : 함수명을 정의하지 않음
- 기명 함수 표현식 : 함수명을 정의함
- 외부에서 함수명으로 호출 불가능
- 내부에서 재귀함수를 호출하는 용도로 사용. 하지만 변수명으로도 호출 가능하여 굳이 쓸 필요는 없어 보임
- 호이스팅 시 변수로 취급되어 선언 부분만 끌어 올림
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function a() {
/* ... */
} // 함수 선언문. 함수명 a가 곧 변수명
a(); // 실행 가능
var b = function () {
/* ... */
}; // (익명) 함수 표현식. 변수명 b가 곧 함수명
b(); // 실행 가능
var c = function d() {
/* ... */
}; // (기명) 함수 표현식. 변수명은 c, 함수명은 d
c(); // 실행 가능
d(); // 실행 불가능. 에러!
함수 선언문의 위험성
- 함수 선언문이 코드 상단에 선언되어 있는 상태에서 동일한 이름의 함수 선언문이 코드 하단에 중복해서 선언 될 경우.
- 호이스팅 과정에서 두 함수 선언문이 모두 끌어 올려지고, 상대적으로 하단에 있는 함수 선언문이 코드 전체에 영향을 미치게 됨.
- 함수 표현식으로 함수를 사용한다면 하단의 함수가 나오기 전까지는 상단에 선언 된 함수로 사용 되다가 하단의 함수가 나오면 그때 부터 하단의 함수로 적용 됨.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
console.log(sum(3, 4)); // (1) : 7을 기대하지만, 3 + 4 = 7이 출력
function sum(x, y) {
// -- 상단 함수
return x + y;
}
var a = sum(1, 2);
console.log(a); // ------- (2) : 3을 기대하지만, 1 + 2 = 3이 출력
function sum(x, y) {
// -- 하단 함수
return `${x} + ${y} = ${x + y}`;
}
var c = sum(1, 2);
console.log(c); // ------- (3) : 1 + 2 = 3이 출력
함수 표현식
- 의도대로 함수가 잘 작동되기위해서 상대적으로 함수 표현식이 안전하다!
- 그 줄아에서 바로 에러가 검출되므로 빠른 타이밍에 손쉽게 디버깅도 가능하기 때문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// console.log(sum(3, 4)); // (1) : sum의 값이 없어서 undefined를 반환하기 때문에 에러!
var sum = function (x, y) {
// -- 상단 함수
return x + y;
};
var a = sum(1, 2);
console.log(a); // ------- (2) : 3 출력
var sum = function (x, y) {
// -- 하단 함수
return `${x} + ${y} = ${x + y}`;
};
var c = sum(1, 2);
console.log(c); // ------- (3) : 1 + 2 = 3이 출력
스코프, 스코프 체인, outerEnvironmentReference
- 스코프 (Scope) : 식별자에 대한 유효 범위
- ES5까지는 전역 공간 외의 오직 함수에 의해서만 스코프가 생성
- 스코프 체인 (Scope chain) : 식별자에 대한 유효 범위를 안에서 부터 바깥으로 차례로 검색해 나가는 것
- 스코프 체인을 가능케 하는 것이 outerEnvironmentReference
스코프체인
- 현재 스코프에 선언 된 식별자가 없으면 상위 스코프로 이동하여 검색
- 여러 스코프에서 동일한 식별자를 선언한 경우에는
무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능
- 변수 은닉화 (Variable shadowing) : 상위 스코프에 선언되어 있지만, 현재 스코프에 이미 선언된 경우에는 값이 할당되어 있지 않다고 하더라도 현재 스코프의 변수를 우선으로 함
1
2
3
4
5
6
7
8
9
10
11
var a = 1;
var outer = function () {
var inner = function () {
console.log(a); // (1)
var a = 3;
};
inner();
console.log(a); // - (2)
};
outer();
console.log(a); // --- (3)
스코프체인-크롬전용
1
2
3
4
5
6
7
8
9
10
var a = 1;
var outer = function () {
var b = 2;
var inner = function () {
// console.log(b); //
console.dir(inner);
};
inner();
};
outer();
전역변수와 지역변수
- 전역변수: 전역스코프에서 선언한 변수 (a, outer)
- 지역변수는 함수 내부에 선언한 변수 (outer내부에 선언한 inner, inner함수에 선언한 함수)