JavaScript 호이스팅 - 비하인드 스토리

이 포스트에서는 자바스크립트에서 호이스팅 메커니즘이 어떻게 발생하는지 이야기하고 싶습니다. 본격적으로 들어가기 전에 호이스팅이 무엇인지 정의해 보겠습니다.

Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.



이 코드를 고려하십시오. 3행의 출력이 무엇인지 짐작할 수 있습니까? :

a = 2;
var a;
console.log( a );


'var a' 문이 a = 2 뒤에 오기 때문에 우리 중 많은 사람들이 'undefined' 로 예상할 것이고, 변수가 재정의되어 기본 undefined 가 할당된다고 가정하는 것이 자연스러워 보일 것입니다. 그러나 출력은 호이스팅으로 인해 2가 됩니다.

따라서 호스팅을 사용하면 코드에 작성하기 전에 변수를 사용하고 함수를 호출할 수 있습니다. 이제 Java와 같은 다른 언어를 사용하는 경우 변수를 사용하기 전에 먼저 정의해야 하는 경우 매우 혼란스러울 수 있습니다.

다른 예를 살펴보겠습니다. 이제 출력을 추측할 수 있습니까?:

console.log( a );
var a = 2;


여기서 출력은 undefined 입니다. 이것은 호이스팅 메커니즘이 선언만 이동한다는 것을 보여주기 위해 계속됩니다. 과제는 제자리에 남아 있습니다.

그러나 이것이 컴파일하는 동안 JS 엔진이 모든 선언을 범위의 맨 위로 이동하도록 코드를 마술처럼 재구성한다는 의미는 아닙니다. 이 동작은 프로그램이 진행하는 두 단계(컴파일 및 실행 단계)의 결과입니다.

컴파일 단계에서 코드 렉싱 및 토큰화 현상이 발생합니다. 이것은 단순히 코드를 a , =2 와 같은 원자 토큰으로 분할한다는 것을 의미합니다. (토큰은 프로그래밍 언어의 단일 요소입니다).
이 단계에서는 할당이나 평가가 수행되지 않습니다.

컴파일러는 선언을 만날 때마다 이를 범위 관리자로 보내 바인딩을 만듭니다. 각 선언에 대해 해당 변수에 대한 메모리를 할당합니다. Just allocates memory, doesn’t modify the code to push the declaration up in the codebase. 그리고 아시다시피 JS에서 메모리를 할당한다는 것은 기본값undefined을 설정하는 것을 의미합니다.

컴파일 단계 후에 실행 단계로 이동합니다. 여기서 엔진이 할당 또는 평가(예: 함수 호출/표현식 평가)를 만날 때마다 바인딩 범위를 묻습니다. 현재 범위에서 찾을 수 없으면 찾을 때까지 상위 범위로 이동한 다음 해당 바인딩으로 평가가 수행됩니다.

따라서 두 번째 스니펫은 다음과 같이 실행됩니다.

Compilation phase or first pass:

console.log(a) // skipped as it is an evaluation

var a = 2; 
/* 
This has two parts -
    1. A declaration part: var a 
    2. An assignment part: a = 2. 
The compiler only deals with the declaration part, 
and allocates memory for variable 'a'. 
The assignment will happen in excecution phase.
*/

Execution phase or second pass:

console.log(a)
/*
console.log() function called with a. 
The engine looks for the variable 'a' in the scope, and finds it,
for now has the value undefined, so prints it.
*/
var a = 2;  
/* 
The engine executes the assignment operation.
Looks for the variable 'a' in the scope chain and finds it.
Assign 2 to it.
*/


이것은 함수 선언에서도 발생합니다. 이 예를 살펴보겠습니다.:

foo();
function foo() {
    console.log( a );
    var a = 2;
}

// Compilation Phase:
foo(); // As this is evaluation, this line is skipped

function foo() {
    console.log( a );
    var a = 2;
}
/* 
The complier sees a declaration with identifier foo, hence memory is allocated to it
As it is a function and a new scope is also created.
It then again encounters a declaration for an indentifier a, so it allocates it to the memory.
*/

// Excecution Phase:
foo();
/* 
The engine looks for the identifier foo in the
scope chain. 
It finds and pull out the value that foo is referencing to- the statements inside it.
() executes the function,and the excecution moves inside foo function
It encounters a call to console.log() with argument a, which at this time is 'undefined'.
It prints undefined.
Execution moves to next line and encouters an assignment.
It looks for identifier a in the function scope and assigns value 2 to it.
The execution moves outside to global scope.
There are no more execution statements, so the program stops.
*/


우리는 또한 우리가 방금 보았듯이 함수 선언은 호이스트되지만 함수 표현식은 그렇지 않다는 점에 유의해야 합니다. 그 이유는 할당은 실행 단계에서 수행되기 때문입니다.

foo(); // not ReferenceError, but TypeError!
var foo = function bar() {
    console.log('Inside bar')
};


여기에서 변수 식별자 foo는 호이스트되어 전역 범위에 연결되므로 foo()는 ReferenceError로 실패하지 않습니다.
그러나 foo는 아직 값이 없으므로 foo()는 정의되지 않은 값을 호출하려고 시도하며 이는 잘못된 연산입니다. 따라서 그것은 던졌습니다 TypeError

Note: Both function declarations and variable declarations are hoisted, but as function declarations take precedence to variable declarations, functions are hoisted first and then variables.



이것은 아래 스니펫으로 설명할 수 있습니다.

foo(); // Output is: 1
var foo;
function foo() {
    console.log( 1 );
}
foo = function() {
    console.log( 2 );
};


let과 const를 이용한 호이스팅



let 및 const로 선언된 변수도 호이스트되지만 var와 달리 변수는 기본값인 undefined로 초기화되지 않습니다. 초기화된 줄이 실행될 때까지 이러한 변수에 액세스하는 모든 코드는 예외를 throw합니다.

키워드let로 선언된 변수는 블록 범위이며 함수 범위가 아닙니다. 변수의 범위가 선언된 블록에 바인딩되어 컴파일 시간 동안 let 로 선언된 변수에 대해 메모리가 할당되지만 파서가 평가할 때만 값으로 초기화된다는 의미입니다.

Note: Unlike var, accessing the variable declared as let or const before the initialization results in a ReferenceError. For var variables, they return a value of undefined if they are accessed before they are declared.



따라서 아래 코드 조각은 ReferenceError를 반환합니다.

console.log(a); 
// Output: ReferenceError: a is not defined ...
console.log(b);
// Output: ReferenceError: b is not defined ...
let a = 10;
const b = 11;


결론적으로 우리는 JS 프로그램이 두 번의 패스로 구문 분석되고 실행된다고 말할 수 있습니다. 이 때문에 호이스팅의 개념이 그림에 나타납니다.

좋은 웹페이지 즐겨찾기