[TIL #56] Hoisting

Whoyoung90·2021년 5월 27일
0
post-thumbnail
post-custom-banner

var 키워드의 경우 let 이나 const 와 달리 변수를 선언하기 이전에도 참조가 가능해지는 문제가 발생한다.

이 문제를 이해하기 위해서 호이스팅이라는 개념을 알고 있어야 한다.

var 의 경우 선언 이전에 참조가 가능하기에 호이스팅이 일어나지만, letconst 는 호이스팅이 일어나지 않는다고 착각 할 수도 있다.

하지만 let , const 을 포함하여 모든 선언은 호이스팅이 일어난다.

> Hoisting?

자바스크립트 엔진 동작을 이해하기 쉽게 하기 위해 만들어진 개념으로

🚀 변수명함수선언이 유효 스코프{} 최상단으로 끌어올려진 후 코드를 실행하는 것처럼 생각하는 것이다.

🚀 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면
함수 선언은 함수 전체를 끌어 올립니다. (발췌: 코어 자바스크립트p.46)

  • 변수 선언, 함수 선언문 => 호이스팅 O

  • 함수 표현식 => 호이스팅 X
    (식별자가 호이스팅될 뿐, 함수 자체는 끌어올려지지 않는다.)

foo(); //(1)
console.log(foo1); //(2)
foo1(); //(3)
function foo() { console.log('함수 선언문'); }
var foo1 = function() { console.log('함수 표현식'); }


//Hoisting-----
var foo1; // 변수 선언이 함수 선언보다 높은 우선순위!
function foo() { console.log('함수 선언문') }
foo();
console.log(foo1);
foo1();
foo1 = function() { console.log('함수 표현식') }

➡️ (1) '함수 선언문' (2) undefined (3) TypeError: foo1 is not a function
실제로 foo1을 출력했을 때는 undefined 를 확인할 수 있으며,
함수로서 foo1() 와 같이 호출했을 때는 TypeError: foo1 is not a function 라는 에러가 발생한다.

> 호이스팅 대상

자바스크립트에서는 ES6에서 도입된 let, const를 포함한 모든 선언을 호이스팅한다.

✅ 변수의 생성 3단계

  • 선언 단계(Declaration): 변수를 변수 객체(Variable Object)에 등록합니다. 이 변수 객체는 스코프가 참조하는 대상이 됩니다.

  • 초기화 단계(Initialization): 변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보합니다. 이 단계에서 변수는 undefined로 초기화됩니다.
    (초기화 단계는 변수 선언문에 도달했을 때 이루어진다.)

  • 할당 단계(Assignment): undefined로 초기화된 변수에 실제 값을 할당합니다.

📌 var 로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다.

👉 초기화 단계에서 undefined로 초기화되기 때문에 변수 선언문 이전에 변수에 접근해도 에러가 발생하지 않고 undefined를 반환한다.

📌 반면 let, const 로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 이루어진다.

👉 따라서, 스코프에 변수를 등록(선언단계)하지만
초기화 단계는 변수 선언문에 도달했을 때 이루어지므로
초기화 이전에 변수에 접근하면 참조 에러(ReferenceError)가 발생한다.

console.log(a); //(1)
var a = 10;

//Hoisting------
var a;
console.log(a);
a = 10;

➡️ (1) undefined

console.log(name); //(1)
console.log(last_Name); //(2)
var name = 'wooyoung';
let last_Name = 'kim';

//Hoisting-------
var name;
let last_Name;
console.log(name);
console.log(last_Name);

➡️ (1) '' (2) Uncaught ReferenceError: last_Name is not defined

Temporal Dead Zone(TDZ)

letconst 로 선언한 변수는 var 처럼 호이스팅이 일어나지만,
var 와 다르게 초기화가 되기전까지 TDZ라는 곳에 머물러 초기화(혹은 할당)이 될 때까지 잠시 '죽어있는 상태'이기 때문에 선언 전에 참조가 불가능한 것이다.
TDZ를 직역해도 '임시로 죽어 있는 공간'이다. TDZ는 선언 전에 변수를 사용하는 것을 비허용하는 개념상의 공간이다.

var 과 달리 선언 전에 변수를 사용하지 못하는 것인데, 호이스팅이 일어난 것을 직접 확인할 수 있을까? 아래 두 예시를 보자.
1)

typeof notDefined; // 'undefined'
typeof variable; // Uncaught ReferenceError: variable is not defined
let variable;

즉, 아예 선언이 되지 않은 변수는 'undefined' 이지만,
let은 TDZ에 있는 식별자에는 접근하는 것은 그 자체로 에러를 발생시키고 있다.

2)

let a = 3; // 전역 변수
{
  console.log(a); // ReferenceError: Cannot access 'a' before initialization
  let a = 10;
}

만약 let 이 호이스팅이 되지 않은 것이라면 2)예시와 같은 에러는 발생하지 않았을 것이다. 중괄호 스코프 내에서 호이스팅이 일어났고 a 가 스코프 내에서 할당이 일어나기 전에 console.log 에서 참조를 하려 했고, 아직 TDZ에 있는 변수 a 를 참조할 수 없기에 ReferenceError 가 발생하는 것이다.

📌 정리하면, let, const도 호이스팅이 이루어진다.
그러나 선언과 초기화가 나누어져있어서 TDZ으로 인해 에러가 발생한다!

추가 예시1)

callAge(1990); // 함수 선언 전에 함수를 호출 -> hoisting 이 되어 작동한다. 

function callAge(year){
   console.log(2021 - year);
} // 함수 선언문

➡️ 31

추가 예시2)

retirement(1990);   // 작동하지 않는다

var retirement = function(year) { 
    console.log(65- (2020- year));
} // 함수의 선언 방식이 아니기 때문에 hoisting 되지 않는다. 
 //(only work on function declaration)

➡️ TypeError: retirement is not a function

> 호이스팅 순위

함수선언이 수식상 위에 작성되어도
👉 변수 선언이 함수 선언보다 높은 우선순위를 가진다.

function myName() {
    console.log("wooyoung");
}
function yourName() {
    console.log("dongyoung");
}
var myName = "robinson";
var yourName = "teddy";

  console.log(myName); // (1)
  console.log(yourName); // (2)

//hoisting
var myName; //변수 선언이 높은 우선순위
var yourName; //변수 선언이 높은 우선순위

function myName() {
    console.log("wooyoung");
}
function yourName() {
    console.log("dongyoung");
}

myName = "robinson";
yourName = "teddy";

➡️ 변수선언 -> 함수선언 -> 마지막으로 값이 robinson,teddy로 할당된다.
(1) robinson (2) teddy

var age = 50;  //global 

console.log(age); //(1)

function foo(){
    var age = 65; //local
    console.log(age);   //(2) local print
}

foo();  
console.log(age);  //(3) global print


//hoisting
var age;
function foo(){
    var age;
    age= 65;
    console.log(age);   //(2) local print
}
age = 50;
console.log(age); //(1)
foo();
console.log(age);  //(3) global print

➡️ (1) 50 (2) 65 (3) 50

profile
비전공으로 일식 쉐프가 되었듯, 배움에 겸손한 프론트엔드 개발자가 되겠습니다 :)
post-custom-banner

0개의 댓글