오늘은 자바스크립트의 호이스팅(hoisting)에 대해 정리해 보려고 한다. 자바스크립트의 변수를 선언하다 보면 종종 이상현상을 확인할 수 있는데, 보통 변수를 선언한 위치보다 위에서 값이 호출되는 현상들이 있다. 이러한 현상을 호이스팅 되었다고 보면 된다. 호이스팅은 영어로 hoist의 뜻이 '끌어올리는'의 의미로 자바스크립트를 실행하게 되면 소스 코드의 최상단으로 변수들을 끌어올리게 된다다. 이러한 현상을 호이스팅이라고 한다.
보통 인터프리터 언어를 개발하다 보면 유사한 현상을 볼 수 있는데, 자바스크립트 언어를 다룰 때 호이스팅은 정말 중요하며, 알고 있는 것과 모르고 있는 것의 차이는 너무 크기 때문에 호이스팅에 대한 다양한 글을 보면서 내용을 이해하는 것이 중요하다.
호이스팅 (hoisting)
호이스팅은 자바스크립트에서 변수와 함수 선언이 해당 스코프의 최상단으로 끌어올려지는 현상을 의미한다. 코드 실행 전에 변수 및 함수 선언이 메모리에 할당되는 것이라고 볼 수 있다.
변수 호이스팅 (Variable Hoisting)
변수 선언은 해당 스코프의 최상단으로 끌어올려진다.
선언된 변수는 할당이 아니라 초기화 단계에서 undefined로 초기화된다.
변수가 선언되기 전에도 참조할 수 있지만 값은 undefined다.
함수 호이스팅 (Function Hoisting)
함수 선언은 해당 스코프의 최상단으로 끌어올려진다.
함수 선언식은 전체 함수가 호이스팅되며, 함수 표현식은 변수만 호이스팅된다.
함수가 선언되기 전에도 호출할 수 있다.
선언과 할당의 분리
자바스크립트 엔진은 코드를 실행하기 전에 변수 및 함수 선언을 해당 스코프의 최상단으로 끌어올린다. 이는 선언된 변수 및 함수가 선언된 위치보다 상단에서 사용될 수 있음을 의미한다.
함수 선언식과 함수 표현식의 차이
함수 선언식은 전체 함수가 호이스팅되어 해당 스코프의 최상단으로 끌어올려지는데, 함수가 선언되기 전에도 호출할 수 있다는 것을 의미한다.
반면 함수 표현식은 변수만 호이스팅되고, 함수는 그 자리에 남게 된다. 따라서 변수가 선언된 후에만 해당 함수에 접근할 수 있다.
전역 스코프에서의 호이스팅
전역 스코프에서 선언된 변수 및 함수는 전역 객체(window 또는 global)에 할당되는데, 이러한 호이스팅은 전역 변수 및 함수가 전역 객체의 프로퍼티로 동작하는 것을 의미한다.
블록 스코프와 변수 호이스팅
ES6에서 도입된 let과 const 키워드는 블록 스코프를 생성한다. 이 변수는 호이스팅 되지만 일시적인 사각지대(temporal dead zone)에서 초기화되기 전까지 참조할 수 없다. 따라서 블록 스코프 내에서의 호이스팅은 변수가 실제 선언된 위치까지만 발생한다.
- 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미
- 영어로 'hoist'는 '끌어올리는'의 의미로 소스 코드의 최상단으로 변수들이 끌어올려짐 (호이스팅 됨)
- 변수를 var로 선언하는 경우 호이스팅이 되어 undefined로 초기화됨
- 변수를 let과 const로 선언하는 경우 호이스팅이 되지 않고 초기화되지 않음
var, let, const 차이
자바스크립트에서는 변수 선언 유형이 var, let, const 이렇게 세 종류가 있고, 각각의 특징과 차이가 있다. 어떤 차이가 있는지 살펴보자.
var
var는 ES5 이전부터 사용되던 변수 선언 키워드다.
함수 스코프를 가지며, 함수 내에서 선언된 var 변수는 해당 함수 내에서만 유효하며, 함수 외부에서는 접근할 수 없다.
호이스팅에 의해 선언부가 스코프의 최상단으로 끌어올려진다.
중복 선언이 가능하며, 이전에 선언된 변수를 재할당할 수 있다.
let
ES6에서 도입된 블록 스코프를 가지는 변수 선언 키워드다.
블록 스코프를 가지므로 {} 내에서 선언된 변수는 해당 블록 내에서만 유효하다.
호이스팅은 일어나지만, 선언부가 스코프의 최상단으로 끌어올려지지 않는다. 따라서 일시적인 사각지대(temporal dead zone)에서 초기화되기 전까지 접근할 수 없다.
중복 선언은 불가능하지만, 재할당은 가능하다.
const
const는 상수를 선언하는 데 사용되는 키워드다.
let과 마찬가지로 블록 스코프를 가지며, 선언된 변수는 해당 블록 내에서만 유효하다.
선언과 동시에 반드시 초기화되어야 하며, 초기화 이후에는 재할당이 불가능하다.
const로 선언된 객체 또는 배열은 수정할 수 있지만, 변수 자체를 재할당하는 것은 불가능하다.
호이스팅이 발생하지만, 일시적인 사각지대에서 변수에 접근할 수 없다.
var : 호이스팅 되는 변수
let, const : 호이스팅 되지 않는 변수
위와 같이 각각의 변수 유형별 차이로 인해 변수의 스코프, 재할당 가능 여부, 초기화 등을 고려하여 적절한 키워드를 선택하여 사용하는 것이 중요하다. 보통은 const를 기본으로 사용하지만, 재할당이 필요한 경우에는 let을 사용하고, var는 최대한 지양하는 것이 좋다.
// hoisting되어 변수 호출 가능
console.log(test);
// var 변수 선언 및 초기값 설정
var test = 'abc';
위의 예제 코드는 var 변수가 호이스팅되는 것을 확인하는 에제 코드다.
var는 자바스크립트에서 가장 기본이 되는 변수 선언 유형이고, 호이스팅이 적용되는 변수 유형이다.
위의 예제를 보게 되면 var 변수를 선언한 라인보다 위에서 해당 변수 test를 호출하고 있다. 보통 프로그래밍에서는 위와 같은 경우에는 에러가 발생하지만 자바스크립트에서의 var 변수는 호출이 가능하다. 위에서 정리한 내용과 같이 이것이 호이스팅(hoisting) 현상이다.
실행 결과는 위와 같이 undefined가 나왔다. 위에서 정리한 것과 같이 호이스팅이 되어 초기값 undefined로 초기화되었다.
조금 응용하여 다시 확인해 보자.
응용 사례
// 값 변경
test = 'a';
// hoisting되어 변수 호출 가능
console.log(test);
// var 변수 선언 및 초기값 설정
var test = 'abc';
위에서 작성한 코드에서 조금 변경한 예제 코드인데, 변수 선언부보다 위에서 값을 변경하고 console을 통해 log를 출력하였다.
위와 같은 코딩이라면 문맥 흐름상 맞지도 않고, 에러가 날 것 같지만 여기서도 동일하게 호이스팅 처리가 되면서 값이 변경된다.
실행 결과는 위와 같이 제일 상단에 작성한 'a'라는 값이 나왔다. 이 부분 역시 호이스팅으로 인해 소스 코드의 최상단에 변수가 초기화되면서 값을 변경할 수 있게 되었다.
그렇다면 자바스크립트 개발을 하면서 꼭 호이스팅 되는 현상을 감수하고 var 변수를 써야 하는지... 이를 대체하기 위해 나온 것이 2015년에 출시한 ES6에서 let과 const의 변수 유형이 나왔다. 두 변수의 성격도 다른 부분이 있지만 우선 var와 비교를 했을 때에는 호이스팅이 되지 않는다는 공통점을 가지고 있다.
let, const 호이스팅
try {
// hoisting되지 않는 변수 호출
console.log(letTest);
// let 변수 선언 및 초기값 설정
let letTest = 'let';
} catch (e) {
console.error(e);
}
try {
// hoisting되지 않는 변수 호출
console.log(constTest);
// const 변수 선언 및 초기값 설정
const constTest = 'const';
} catch (e) {
console.error(e);
}
위의 코드는 let과 const를 사용해서 작성한 예제 코드다. var를 사용할 때와는 다르게 선언부보다 위에서 호출하게 되면 에러가 발생할 수 있다. 그래서 try/catch 예외 처리를 하여 에러가 발생하는 것을 확인하였다.
결과는 위와 같이 해당 변수에 access 할 수 없다고 나온다다. 결론은 let, const는 호이스팅은 되지만 변수를 선언한 선언부 하위에서 사용해야 되는 것이며, var 변수의 단점을 보완하기 위해 나온 것이라고 볼 수 있다.
추가로 let과 const의 차이로는 let은 값의 변경이 가능하고, const는 값의 변경이 불가능한 상수의 성격을 가지고 있다.
- let : 값 변경 가능
- const : 값 변경 불가능 (상수)
호이스팅의 주의사항
호이스팅은 자바스크립트에서 변수 및 함수 선언이 스코프의 최상단으로 끌어올려지는데, 이러한 동작은 코드의 실행 전에 처리되므로 개발자가 예상하지 못한 동작을 초래할 수 있다. 따라서 자바스크립트를 개발한다면 호이스팅의 주의사항을 고려하는 것이 중요하다.
변수 할당과 선언의 분리
호이스팅은 변수의 선언을 최상단으로 끌어올리지만, 변수에 할당된 값은 끌어올려지지 않는다. 따라서 변수를 선언한 후 나중에 값을 할당하는 경우, 초기화되기 전에 변수에 접근할 수 있으므로 주의해야 한다.
함수 호이스팅
함수 선언식은 전체 함수가 스코프의 최상단으로 끌어올려지기 때문에 함수를 선언하기 전에 호출할 수 있다. 하지만 함수 표현식은 호이스팅되지 않으므로 주의가 필요하다.
test(); // "Hello, world!"
function test() {
console.log("Hello, world!");
}
test2(); // TypeError: bar is not a function
var test2 = function() {
console.log("Hello, world!");
};
위의 예제 코드와 같이 일반적으로 함수를 선언한다면 선언 위에서도 호출이 가능하지만, 아래와 같이 함수 표현식으로 함수를 선언한다면 호이스팅이 되지 않는다.
블록 스코프 변수
let과 const는 블록 스코프를 가지므로 호이스팅되지만 일시적인 사각지대(temporal dead zone)에 빠지게 된다. 이로 인해 초기화되기 전에 접근하면 ReferenceError가 발생할 수 있다.
함수 표현식과 변수 이름 충돌
함수 표현식에서 함수 이름을 사용하면 호이스팅으로 인해 변수와 함수 이름이 충돌할 수 있다. 따라서 함수 표현식을 사용할 때에는 함수 이름을 생략하거나 고유한 이름을 사용해야 한다.
var test = function test2() {
console.log(typeof test2); // function
};
test();
함수 호이스팅 순서에 따른 영향
여러 함수가 동일한 이름으로 선언되는 경우에는 호이스팅은 선언된 순서에 따라 동작하는데, 이 때문에 동일한 이름의 함수가 덮어씌워지는 경우가 발생할 수 있다. 그렇기 때문에 함수의 순서와 이름을 신중하게 결정해야 한다.
test(); // "안녕하세요!"
function test() {
console.log("안녕하세요!");
}
function test() {
console.log("안녕히계세요!"); // 이전 함수가 덮어씌워짐
}
위와 같이 두 번째 선언된 함수로 덮어씌워지게 된다.
중첩 함수의 호이스팅
함수가 다른 함수 내에서 선언되는 경우에는 내부 함수의 선언도 외부 함수와 함께 호이스팅된다. 이는 코드의 가독성을 저해할 수 있으므로, 중첩 함수를 사용할 때에는 호이스팅이 발생하는지 주의해야 한다.
function outer() {
console.log(inner()); // "Hello, inner function!"
function inner() {
return "Hello, inner function!";
}
}
outer();
마무리
오늘은 이렇게 자바스크립트의 호이스팅에 대해 정리해 봤다. 자바스크립트는 다른 언어와 다르게 호이스팅이라는 동작이 발생한다. 인터프리터 언어이면서 UI에서 동적으로 처리되는 언어인 만큼 신경써야하는 부분들이 있고, 그 중에서 호이스팅은 아주 중요한 개념이라고 생각한다. 요즘은 기술의 발전으로 PC의 성능이 많이 좋아져서 크게 차이 없을 수도 있지만, 자바스크립트를 학습하는 단계라면 이러한 개념들을 정리해 두는 것도 중요하다고 생각한다. 기본을 잘 다지는 만큼 자바스크립트 기반의 새로운 기술들이 나오더라도 도움이 많이 될 것이라고 생각한다.
'javascript > javascript' 카테고리의 다른 글
[javascript] 자바스크립트 Failed to execute 'replaceState' on 'History': A history state object with URL 에러 해결하기! (0) | 2023.01.20 |
---|---|
[javascript] 자바스크립트 배열 다루기 (0) | 2023.01.19 |
자바스크립트 JSON.parse(), JSON.stringify() 함수의 특징과 활용 (0) | 2023.01.16 |
[javascript] 자바스크립트 JSON Object 비교하기 (값비교, 두 JSON 비교) (0) | 2023.01.15 |
[javascript] 자바스크립트 "[object Object]" is not valid JSON (syntaxError 문법 에러 처리) (0) | 2023.01.14 |