2024. 3. 6. 01:47ㆍ자바스크립트
https://ukgi-dev.tistory.com/31
해당 내용은 실행 컨텍스트 학습이 선행되어야한다.
렉시컬 환경의 환경 레코드에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
식별자 정보에는 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 함수 선언문, 변수의 식별자 등이 있다.
이렇게 컨텍스트 전체를 쑥 훑으면서 식별자 정보를 순서대로 수집을 한다.
식별자 정보를 수집하는 과정은 아직 코드가 실행되기 전이다.
즉, 자바스크립트 엔진은 이미 해당 실행 컨텍스트에 속한 코드의 변수명을 모두 알고 있게 된 것이다.
다른 표현으로 식별자들을 모두 최상단으로 끌어올린 다음 실제 코드를 실행한다고 봐도 무방하다.
진짜 식별자들을 엔진이 올리는 것은 아니다. 편의상 끌어올린 것으로 간주하자는 것이다. 이것이 호이스팅이다.
호이스팅은 자바스크립트 엔진이 컨텍스트 내부의 모든 식별자 정보를 끌어올린다고 간주하는 가상의 개념이다.
function a(x) {
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
처음에는 1, undefined, 2가 출력될 것이라 예상했지만 결과는 1, 1, 2이다.
호이스팅을 이해하지 못하면 왜 이렇게 출력되는지 전혀 알 수 없다.
function a(x) {
var x = 1;
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a();
먼저 인자를 함수 내부의 다른 코드보다 먼저 선언과 할당이 이루어졌다고 간주해보자.
이렇게 보아도 되는 이유는 렉시컬 환경이 생성될 때 arguments 정보에 이미 함수의 인자로 들어온 값이 저장되어있기 때문에 렉시컬 환경 입장에서는 이렇게 코드를 변경해도 무방하다.
이 상태에서 호이스팅을 해보자.
환경레코드는 어떤 식별자가 있는지만 관심있고 무슨 값이 할당되는지는 관심없다.
따라서 변수명만 위로 올리고 할당 과정은 원래 자리에 둔다. 매개변수도 마찬가지이다.
function a(x) {
var x;
var x;
var x;
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
이제 호이스팅이 끝났고 실제 코드가 실행되므로 1, 1, 2가 출력된다.
function a() {
console.log(b);
var b = "bbb";
console.log(b);
function b() {}
console.log(b);
}
a();
위의 예제와 다른 점은 내부에 함수 선언문이 있다는 것이다.
실행 컨텍스트가 생성되고 환경레코드는 변수명과 함수 정보를 수집한다.
이때, 변수는 선언부와 할당부를 나누어 선언부만 수집하고 함수 선언은 함수 전체를 수집한다.
(수집한다는 표현은 끌어올린다는 표현과 같다.)
그리고 함수 선언문은 변수 선언부보다 호이스팅의 우선순위가 높다.
function a() {
function b() {}
var b;
console.log(b); // ✅
b = "bbb";
console.log(b);
console.log(b);
}
a();
✅표시한 부분에 함수가 출력되는 이유는 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것으로 표현할 수 있기 때문이다. 즉, 함수 선언문에서 함수명 b는 곧 변수명이 된다.
var b = function b() {};
var b;
그리고 이미 b에 함수가 할당되어있기 때문에 아래에 있는 b는 무시되어 함수가 그대로 출력된다.
함수 선언문과 함수 표현식
함수 선언문은 함수 정의부만 존재하고 별도의 할당 명령이 없다.
함수 표현식은 정의한 함수를 별도의 변수에 할당하는 것을 말한다.
함수 선언문은 반드시 함수명이 정의돼 있어야하지만 함수 표현식은 그럴 필요가 없다.
function a() {} // 함수 선언문. 함수명 a가 곧 변수명
const b = function () {}; // 함수 표현식. 변수명 b가 곧 함수명
const c = () => {}; // 화살표 함수
두 개념의 실질적인 차이는 무엇이 있을까?
변수는 선언부와 할당부를 나누어 선언부만 수집하고 함수 선언은 함수 전체를 수집한다.
이 개념을 기억하자.
console.log(sum(1, 2));
console.log(multiply(3, 4));
function sum(a, b) {
return a + b;
}
const multiply = (a, b) => {
return a * b;
};
ReferenceError: Cannot access 'multiply' before initialization
참조오류가 발생하는데 비밀은 호이스팅에 있다.
multiply는 함수 표현식이기 때문에 환경 레코드가 정보를 수집하는 과정에서 함수 전체를 수집하는 것이 아니라 선언된 변수명만을 수집한다. 따라서 호이스팅을 마친 최종 상태는 다음과 같다.
function sum(a, b) {
return a + b;
}
const multiply;
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = (a, b) => {
return a * b;
};
정리
- 함수 표현식은 변수명만 호이스팅 된다. 함수 자체가 호이스팅 되지 않는다.
- 함수 표현식이 함수 선언문보다 안전하다.
- 함수와 변수 선언문 중에는 함수 선언문이 먼저다.
func();
var func = function () {
console.log("변수 호이스팅");
};
function func() {
console.log("함수 호이스팅");
}
After Hoisting
function func() {
console.log("함수 호이스팅");
}
var func;
func();
func = function () {
console.log("변수 호이스팅");
};
Result : "함수 호이스팅"
'자바스크립트' 카테고리의 다른 글
네이티브 객체 vs 호스트 객체 (0) | 2024.03.26 |
---|---|
브라우저의 렌더링 과정 (0) | 2024.03.25 |
스코프 (0) | 2024.03.03 |
이벤트 위임 (0) | 2024.03.01 |
Ajax (0) | 2024.03.01 |