Hoisting에 대한 오해

Jaemin Jung·2022년 11월 13일
0

JavaScript

목록 보기
14/14
post-thumbnail

들어가며

최근 JS 스터디를 시작하면서 (모던 자바스크립트 딥 다이브) 잘못 이해하고 있었던 부분이 있어서 글로 정리해봤다.

Hoisting

다음과 같은 코드가 있다.

const num = add(10, 20);

console.log(num); // 30

function add (a, b) {
  return a + b;
}

숫자 두 개를 인자로 받아 두 값을 더한 값을 반환하는 add라는 함수를 실행해서 변수에 저장한다.

그 밑에서 실행 결과를 담은 num을 출력하고 실제 add 함수를 그 밑에서 선언한다.

여기서 주목할 부분은 add 함수 선언이 코드의 가장 마지막 부분에서 선언 되었다는 것이다.

일반적으로 코드는 위에서 아래로 실행이 된다.

하지만 add 함수를 실제 실행하는 가장 상단의 코드에서는 add 함수가 선언 된걸 구경도 못했을텐데 이게 실행이 된다.

이러한 현상을 Hoisting 이라고 부른다.

하나의 예제 코드를 더 살펴보자

console.log(tvChannel); // ?

var tvChannel = "Netflix";

console.log(tvChannel); // Netflix

이번엔 tvChannel 이라는 변수가 var 키워드로 선언되기 이전에 해당 변수룰 console.log를 실행 한다.

다른 언어에서는 에러가 발생할 수 있지만, Js에서는 에러를 발생시키지 않는다.

Hoisting 때문이다.

  • 실행 결과
console.log(tvChannel); // undefined

var tvChannel = "Netflix";

console.log(tvChannel); // Netflix

Hoisting에 대한 보통의 오해

위 코드의 실행 결과만 봤을 땐, 첫 번째 console.log 전에 이미 tvChannel 변수가 만들어진 것처럼 보인다.

이러한 현상의 명칭이 Hoisting 이라는 것도 그렇고, 변수 선언을 코드 상의 최상단으로 끌어올려지는 현상이라고 오해하기 쉽다.

내가 그랬다ㅋ;

하지만, 이는 선언 전에 Js에서 미리 변수를 물리적으로 최상단에 끌어 올렸기 때문이 아니다.

JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다. 반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.

컴파일러나 인터프리터를 만든 개발자들이나, 시니어 개발자들이 이러한 현상을 비전공자나 주니어 개발자에게 설명해주기 위해 Hoisting(직역하면 끌어올리다)이라는 이름을 붙여 쉽게 접근할 수 있도록 노력을 해왔다고 한다.

아무튼 Hoisting 덕분에 변수(var)나 함수를 선언 전에 사용할 수 있다.

하지만 이는 맥락상 정말 중요한 차이가 있다.

Hoisting은 언어를 사용하는 사람들의 편의를 위해 추가적으로 구현한 것이 아니라, 언어를 Hoisting 되게 하는게 더 쉬워서 그냥 내버려 둔 것이다.

이러한 현상을 위해 뭔가 더 만들어 내서 Hoisting 되도록 한게 아니라 오히려 그냥 아무것도 안했기 때문에 Hoisting이 되는 것이라는 것.

Hoisting 과정

실제 Hoisting 현상이 어떻게 진행되는지 확인해보자.

var a = 10;
let b = 20;
const c = 30;
function test() { ... }
const num = add(10, 20);
                 
// 실제로는 더 복잡하겠지만 추상적으로 풀어봤다.
=> 
Dictionary of
a    | var      | 10
b    | let      | 20
c    | const    | 30
test | function | ...

Js 코드를 실행하면 코드를 실행 전에 일단 선언이 된 함수나 변수가 뭐가 있는지 스캔을 먼저 하고 일종의 사전 같은 곳에 미리 모아준다.

그리고 필요할 때 마다 사전에서 값을 찾아 사용하는 것.

이렇게 미리 만들어 두고 쓰기 때문에, 선언을 나중에 하더라도 사용이 가능하다는 것이다.

이러한 값을 저장하는 사전 같은곳을 Record라고 부르는데,

ECMA Script 스펙에 나오는 정식 명칭은 Environment Record 라고 하며,

식별자와 식별자에 바인딩된 값을 기록해두는 객체이다.

Record에 선언문만 먼저 실행해서 Environment Record에 기록하는 것을 생성 단계라고 한다.

하지만 변수에다가 값을 넣는건(할당) 미리 불러올 수 없기 때문에,

값을 미리 불러다가 쓸 수 없다. (함수 선언식 예외)

Variable hoisting

Hoisting에 대해 다시 공부하게 되면서 오해했던 부분이 하나 더 있다.

var 키워드만 Hoisting이 일어나는걸로 착각한 것.

var, let, const 키워드로 선언된 변수는 모두 Hoisting이 일어난다.

var 키워드는 Hoisting이 일어나며 선언 이전에는 생성 단계에서 초기화(undefined)까지 진행되고,

let const 키워드는 선언 이전에는 생성 단계에서 초기화를 진행하지 않아 변수 값을 사용 할 수 없다.

이를 일시적 사각 지대 TDZ(Temporal Dead Zone)라고 한다.

console.log(gender); // undefined

console.log(name); // Reference error
// ... 여기까지 name 변수의 TDZ
const name = 'James';
// ... 여기까지 age 변수의 TDZ
console.log(age); // Reference error

let age = 29;

console.log(name, age) // 'James', 29

var gender = 'Male'

console.log(gender) // 'Male'

이처럼 var 키워드와 달리 비교적 최근에 추가된 let const 키워드는 선언 전에 접근할 수 없는 것은

Js 에서도 선언 라인 이전에는 변수를 참조할 수 없다.는 일반적인 프로그래밍 방식을 추구할 수 있도록 언어 차원에서 보완되었다.

Function Hoisting

  • 함수 선언문
    function 키워드를 이용해 함수를 선언하는 것
function add (a, b) {
 return a + b
}
  • 함수 표현식
    변수에 함수 선언을 담는 것 (실제로는 Variable hoisting에 해당됨)

const add = function (a, b) {
 return a + b
}

// Or

const add = (a, b) => a + b

Js에서 함수 선언문은 Hoisting이 일어나지만, 함수 표현식은 Hoisting이 일어나지 않는다. (정의하기 전에 사용할 수 없다.)

해당 코드를 실행하게 되면 에러가 발생하게 된다.

foo() // TypeError: foo is not a function
boo() // Reference Error

var foo = function() {
  // Do somethings..
}

const boo = function() {
  // Do somethings..
}

선언 전에 var 키워드는 Hoisting 된다.

foo 함수를 실행하는 시점에는 Environment Record에 기록되어있는 foo 변수의 값은 undefined이고

undefined라는 데이터 타입은 함수와 달리 실행할 수 없기 때문에 타입 에러가 발생하게 되는 것

const 키워드는 Environment Record에 기록되어있는 값이 없어 Reference Error가 발생하게 된다.

foo() // Do somethings..


function foo () {
  // ... Do somethings..
}

반대로 함수 선언문 경우에는 다르다.

function 키워드로 선언된 foo 함수는 코드 실행시 Hoisting이 일어나는 것은 동일하다.

이 때 표현식과 다른점은 완성된 함수 객체를 생성해서 Environment Record에 기록한다는 점이다.

그래서 함수 선언식은 선언 전에 함수를 실행해도 함수를 정상적으로 사용할 수 있게 된다.

profile
내가 보려고 쓰는 블로그

0개의 댓글