실행할 코드에 제공할 환경 정보들을 모아놓은 객체.
스택(stack) : 출입구가 하나뿐인 마치 항아리같은 데이터 구조이다. 마지막에 쌓인 데이터를 가장 먼저 꺼낼 수 있다.
큐(queue) : 출입구가 양쪽으로 모두 열려있는 마치 파이프같은 데이터 구조이다. 데이터가 쌓인 순서대로 처리한다.
하나의 실행 컨텍스트를 구성할 수 있는 방법으로, 전역공간, eval() 함수, 함수 실행, 블록(ES6+) 등이 있다.
VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷으로 변경 사항은 반영되지 않음.
LexicalEnvironment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영된다.
ThisBinding : this 식별자가 바라봐야 할 대상 객체.
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수 이름, 함수 선언, 변수명 등)들이 저장된다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다.
전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체인 전역 객체를 활용한다.
위의 말대로면, 코드가 실행되기 전인데도 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알게 된다. '자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'라고 생각해도 코드를 해석하는 데는 문제될 것이 전혀 없을 것이다. 여기서 호이스팅이란 개념을 떠올릴 수 있다.
호이스팅은 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념이다.
호이스팅을 이해하기 쉽게 코드로 표현해 볼 것이다. 이 코드는 실제 엔진이 이러한 변환 과정을 거친다는 것이 아닌, 이해하기 쉽게 표현하기 위함이다.
function a () {
console.log(b);
var b = 'bbb';
console.log(b);
function b () { }
console.log(b);
}
a();
위의 코드를 그대로만 해석한다면 console에는 1. 에러 or undefined 2. 'bbb' 3. b 함수가 출력될 것으로 예상될 것이다. 하지만 틀렸다. 호이스팅 되기 때문이다.
설명대로의 코드를 다시 표현해보겠다.
function a () {
var b; // 수집 대상 1. 변수는 선언부만 끌어올린다.
function b () { } // 수집 대상 2. 함수 선언은 전체를 끌어올린다.
console.log(b);
b = 'bbb';
console.log(b);
console.log(b);
}
a();
여기서, 호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있다.
아래의 코드로 쉽게 이해해보자.
function a () {
var b;
var b = function b () { } // 함수명으로 선언한 변수에 함수를 할당.
console.log(b);
b = 'bbb';
console.log(b);
console.log(b);
}
a();
위에서 예상한 답은 1. 에러 or undefined 2. 'bbb' 3. b 함수 였지만, 실제로는 1. b 함수 2. 'bbb' 3. 'bbb'가 실행되는 것을 볼 수 있다.
함수 선언문
1. function 정의부만 존재하고 별도의 할당 명령 없음
2. 반드시 함수명이 정의되야함.
3. 전체를 호이스팅한다.
함수 표현식
1. 정의한 function을 별도의 변수에 할당한다.
2. 함수명이 있어도, 없어도 된다.
(함수명 정의한 것 : 기명 함수 표현식, 정의하지 않은 것 : 익명 함수 표현식)
3. 변수는 선언부만 끌어올리고, 할당부는 원래 자리에 남겨진다.
4. 기명 함수 표현식은 외부에서는 함수명으로 호출할 수 없고, 함수명은 오직 함수 내부에서만 접근할 수 있다.
// 함수를 정의하는 세 가지 방식
function a () { ... } // 함수 선언문. 함수명 a가 곧 변수명.
a(); // 실행 ok.
var b = function () { ... } // 익명 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 ok.
var c = function d() { ... }
c(); // 실행 ok.
d(); // 에러!
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수 이름, 함수 선언, 변수명 등)들이 저장된다.는 설명을 다시 한번 되새겨보자. 호이스팅 시 변수와 함수 선언을 상단으로 끌어온다. 그 말은 즉, 함수 선언문은 전체를 호이스팅하고 함수 표현식은 변수 선언부만 호이스팅한다.
함수 선언문으로 코드를 작성하면 선언 전에도 호출하여 실행할 수 있지만, 함수 표현식은 선언된 이후에 호출하여 실행할 수 있다.
동일한 변수명에 서로 다른 값을 할당하는 경우 나중에 할당한 값이 먼저 할당한 값을 덮어씌운다.
만약, 동일한 변수명에 함수 선언문으로 코드를 작성 시 나중에 할당한 함수가 덮어씌워져 실행되므로 에러를 발생시킬 수 있다.
오늘은 실행 컨텍스트와 호이스팅에 대해 공부해보았다. 쉽기도 했지만 헷갈리는 부분이 많았던 것 같다. 하지만 개념을 제대로 알고나니 코드를 보는 시야가 넓어졌다는 느낌이 들었다.
특히, 함수 선언문과 함수 표현식에서 강한 충격을 받았다. 이 개념을 모른 채로 현업에 들어가 내 맘대로 코드를 작성한다면?.. 끔찍한 에러들을 보았을 것이다.
그런 실수를 경험하기 전에 개념들을 익혀둘 수 있어서 정말 다행이라고 생각했다!