: JS 엔진이 변수 정보를 수집하는 과정을 이해하기 쉬운 방법으로 대체한 가상의 개념.
environmentRecord는 실행 컨텍스트에서 실행하는 정보 중, VariableEnvironment & LexicalEnvironment 를 구성하는 정보 중 하나. 현재 컨텍스트와 관련된 코드의 식별자 정보 등이 저장된다.
→ 식별자 : 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자
, 선언한 함수 자체
, var로 선언된 변수의 식별자
등.
"JS엔진은 실행 컨텍스트가 관여할 코드들을 실행하기 이전에 변수 정보 수집을 끝마치고 있는 레디 상태임" ⇒ 이 개념을 식별자들을 최상단으로 끌어올려(hoist)놓은 다음 실제 코드를 실행한다는 개념으로 이해하기 쉽게 해석한 것이 바로
Hoisting
BEFORE 호이스팅
function hello(x) { //수집대상 1(매개변수)
console.log(x);
var x; //수집대상 2(선언된 변수)
console.log(x);
var x = 2; //수집대상 3(선언된 변수)
console.log(x);
}
hello(1);
⬇
AFTER 호이스팅
function hello( ) {
var x; //수집대상 1(매개변수)
var x; //수집대상 2(선언된 변수)
var x //수집대상 3(선언된 변수)
x = 1; //함수 호출시 매개변수에 할당된 값
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
hello(1);
BEFORE 호이스팅
function a () {
console.log(b); // (1)
var b = 'bbb'; //수집대상 1(선언된 **변수**)
console.log(b); //(2)
function b () {} //수집대상 2(선언된 **함수**)
console.log(b); //(3)
};
a();
⬇
AFTER 호이스팅
function a () {
var b; //수집대상 1(선언된 **변수**) ====> 변수는 선언부만 끌어올려짐(hoisted)
function b () {} //수집대상 2(선언된 **함수**) ===> 함수는 전체가 끌려올라감
console.log(b); // (1)
b = 'bbb';
console.log(b); //(2)
console.log(b); //(3)
};
a();
⚠️ 변수는 선언부만 hoisting되고, 함수는 함수 전체가 hoisting되는 이유?
: JS창시자인 브랜든 아이크가 JS를 유연하고 쉬운 언어로 만들고자 했기 때문. 덕분에 함수를 선언한 위치와 무관하게 실행할 수 있게 됐지만 많은 혼란도 함께 야기됨.
함수를 정의하는 3가지 방식
(1)함수 선언문 (2) 익명 함수 표현식 (3) 기명 함수 표현식
//(1) 함수 선언문
function a () { //do something } //**함수명 a = 변수명**.
//(2)익명 함수 표현식
var b = function () { //do something } //**변수명 b = 함수명**.
//(3)기명 함수 표현식
var c = function d() { //do something } //**변수명 c, 함수명 d (같지 않음)**
❖ 과거에는 디버깅 용이하게 할 목적으로 기명함수를 사용했었으나 이제는 브라우저들이 똑똑해져서 굳이 필요하지 않다고 함.(+기명 함수 표현식의 함수명(d)으로는 함수 내부에서만 호출 가능하고 외부에서는 호출 안 됨😕 )
BEFORE 호이스팅
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;
}
⬇
AFTER 호이스팅 후
function sum(a,b) { // 함수 선언문
return a + b;
}
var multiply; //함수 표현식 - 변수명
console.log(sum(1,2));
console.log(multiply(3,4));
multiply = function (a,b) { //변수 할당부인 실제 함수 부분은 제자리에 남음.
return a * b;
}
⚠️ 함수 선언문의 위험성 예시
😎 선임이 a 함수를 1번째 줄에서 함수선언문
으로 선언해놓음
🤓 (호이스팅 개념 없는)신입이 10,000번째 줄에서 함수선언문
으로 a 함수의 내용을 수정해버림.
→ 신입은 1~9999번째 줄까지는 수정한 함수가 영향을 끼치지 않을 거라고 생각했지만, 함수선언문의 함수 전체가 호이스팅 되고 선임이 짠 코드가 다 어그러져버림!
⇒ 만약 선임과 신임 둘다 함수 표현식
으로 함수를 정의했다면 문제가 없었을 것!
let이 호이스팅되지 않는 "것처럼" 보이는 예시
console.log(name);
//ReferenceError: name is not defined
let name = 'seo';
→ name 선언 이전에 호출시 reference error가 나서 let은 호이스팅이 되지 않는다고 생각할 수 있음.
🔺 but, 호이스팅 되지 않아서가 아니라 let의 변수 생성과정이 var와 다르기 때문임
- var, let, const의 변수 생성과정 비교
⇒ let으로 선언한 name은 맨 위로 선언부가 호이스팅이 되긴 됐으나, 초기화(메모리 할당)가 아직 되지 않았기 때문에 reference error가 나게 되는 것.
+)
let & const 로 선언된 변수는 블록 레벨 스코프를 가짐. 즉, {} 내부에 변수를 선언하면 해당 블록 내부에만 생명주기가 유지됨. 반대로 var는 함수 레벨 스코프를 가지므로, 블록 내부에 선언되어도 외부에서 접근할 수 있음.