관리 메뉴

꾸준히 합시다

스코프와 호이스팅 Scope and Hoisting 본문

자바스크립트 기초

스코프와 호이스팅 Scope and Hoisting

tturbo0824 2021. 12. 3. 11:06

스코프란?

자바스크립트에서 스코프는 변수가 유효할 수 있는 범위, 즉 변수에 접근할 수 있는 범위를 뜻한다.

 

스코프는 크게 지역 스코프(Local Scope)와 전역 스코프(Global Scope)로 나눌 수 있다. Global Scope는 최상단의 스코프로써 이곳에서 선언된 변수(전역 변수)는 어떤 영역에서든 접근이 가능하다. Local Scope는 Global Scope에 포함되어 있는 영역으로 이곳에서 선언된 변수(지역 변수)는 전역(Global)에서 선언된 변수보다 더 높은 우선순위를 가지게 되며 Local Scope에서 선언된 변수는 Global Scope에선 참조가 불가능하다는 특징을 가지고 있다.

 

지역 변수 (함수 수준 범위)

var message = "Hello";

function sendMessage() {
     var message = "Goodbye"; // 지역 변수, sendMessage() 함수에서만 접근 가능
     console.log(message);
}

sendMessage(); // Goodbye
console.log(message); // Hello : 전역 변수

 

함수 수준 범위 잘못된 예시

var message = "Hello";

if (message) {
     message = "Goodbye";
     console.log(message); // Goodbye : 전역 변수
}

console.log(message); // Goodbye

 

위처럼 지역 변수를 선언하지 않는다면 문제를 일으킬 가능성이 높아지게 되는데, 아래의 케이스도 그 때문에 문제가 발생한 경우이다.

var message = "Hello";

function saySomething() {
    console.log(message);
}

function sayGoodbye() {
    message = "Goodbye";
    console.log(message);
}

saySomething(); // Hello

// message는 전역변수 message를 변경해 버린다.
sayGoodbye(); // Goodbye

// 이제 전역 변수 message는 Goodbye이게 되고 더 이상 Hello는 출력되지 않는다.
saySomething(); // Goodbye

// 이를 해결하기 위해서는 함수 내에서 지역 변수 선언 시 var 키워드를 사용하는 것.
function sayGoodbye() {
    var message = "Goodbye"; // 여기서 message는 전역 변수가 아니라 항상 지역변수이게 된다.
    console.log(message);
}

지역 변수는 함수 내에서 전역 변수보다 높은 우선순위를 가진다. 즉 같은 이름의 전역 변수와 지역 변수가 존재할 경우 이 변수가 함수 내에서 사용된다면 지역 변수가 우선권을 가지게 되는 것이다.

 

 

호이스팅이란?

호이스팅은 코드에 선언된 변수 및 함수를 끌어올려 유효 범위의 최상단에 선언하는 것을 말한다. 즉 함수 내에서 선언한 함수 범위(function scope)의 변수는 해당 함수의 최상단으로, 함수 밖에서 선언한 전역 범위(global scope)의 전역 변수는 스크립트 단위의 최상단으로 끌어올려지게 된다.

 

// 함수 먼저
function sayHello() {
    console.log('Hello');
}

sayHello();

// 함수의 호출을 먼저
sayGoodbye();

function sayGoodbye() {
    console.log('Goodbye');
}

/*
output:

Hello
Goodbye
*/

위와 같이 자바스크립트에선 함수를 선언하기 전 함수를 먼저 호출해도 무리 없이 함수가 실행된다. 이러한 현상을 호이스팅이라 하는데 이 때문에 동작의 오류처럼 느껴지는 여러 상황들이 일어나게 된다.

 

day = 27;
day++;
console.log(day); // output: 28

var day;

분명히 변수의 선언을 아래에서 했는데도 로직이 제대로 동작함을 확인할 수 있다. 

 

console.log(month);

month = 'March';

console.log(month);

var month;

/*
output:
undefined
March
*/

month에 아무 값도 할당하지 않고 출력했을 때는 undefined가, 값을 할당하고 다시 출력하게 되면 제대로 된 값이 출력된다.

 

console.log(month);

month = 'March';

console.log(month);

var month = 'February';

console.log(month);

/*
// 위와 동일한 로직
var month;

console.log(month);

month = 'March';

console.log(month);

month = 'February';

console.log(month);
*/

/*
output:
undefined
March
February
*/

인터프리터가 코드를 로드할 때, 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되어 변수의 선언을 항상 컨텍스트 내의 최상위로 끌어올려지게 된다. 변수의 선언부만 최상단으로 올라가기 때문에 여전히 첫 번째 출력에 대한 값으로는 undefined가 나온다.

 

이처럼 var을 사용하게 되면 호이스팅 현상 때문에 여러 혼란이 야기될 수 있지만 예약어 let을 사용해 변수를 선언하면 이러한 문제는 해결된다.

 

let을 사용한 변수는 호이스팅이 되기는 하지만 초기화가 이루어지지 않은 상태(Uninitialized)에서 호이스팅이 되기 때문에 초기화 단계를 만나기 전에는 참조할 수가 없다. 때문에 초기화 이전에 변수에 접근하려고 하면 참조 에러(Reference Error)가 발생한다. 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 이 사각지대를 Temporal Dead Zone(TDZ)이라고 한다.

Comments