렉시컬 환경을 설명하며 호이스팅에 간단하게 언급했었다. (https://bysxx.tistory.com/3)
렉시컬 환경을 간단하게 요약하자면 자바스크립트를 실행하며 코드 실행에 필요한 정보가 함수 단위로 쌓이는 객체를 실행 컨텍스트라고 하고 여기에는 렉시컬 환경이 있는데 여기에는 함수 내부의 정보를 가진 environmentRecord, 함수 외부의 정보를 가진 outer-EnvironmentReference가 있다.
여기서 environmentRecord <- 요너석이 호이스팅을 일으키는 요소로 함수 내부의 정보를 수집하는 과정에서 함수 내부에 선언된 함수, 변수가 전부 끌어올려지게 된다.
엥 쉬운데? 라고 생각할 수 있지만 막상 다른 언어에 익숙해졌다면 디버깅 시 매우 어지럽다
예시를 적어보자면
var b = 'out'
function foo (a) {
console.log(a, b)
var b = 'in'
}
foo('test')
흠 b가 외부에서 선언되어 있고 콘솔로그 아래에서 재선언 하고 있으니 콘솔로그에는 'test out'이라고 찍히겠지?
아쉽게도 그렇지 않다 foo라는 함수가 실행되자마자 environmentRecord에 모든 변수가 저장되며 끌어올려져 아래와 같이 변한다고 생각하면 된다.
var b = 'out'
function foo (a) {
var a = 'test'
var b
console.log(a, b) // test undefined
b = 'in'
}
foo('test')
음? 호이스팅은 이렇게 논리적으로 예측되는 범위 밖 상황을 만들기 때문에 정확하게 이해하는 것이 중요하다.
한 번 더 어지러운 상황을 예제로 만들어보자면
function addAndPrint (a, b) {
console.log(a + b)
}
function foo (a) {
addAndPrint(a, 2)
function addAndPrint (a, b) {
console.log(a + b, '개')
}
addAndPrint(a, 2)
}
foo(1)
여러가지로 이 함수의 결과를 예측해 볼 수 있을 것 같다.
1. 위에서 호출한 addAndPrint는 3을 아래에서 호출한 addAndPrint는 3 개?
2. 이전 예시 처럼 addAndPrint가 foo가 실행될 때 끌어올려져 undefined이라 함수가 아니라고 에러를 뱉을까?
아쉽게도 정답은 둘다 아닌 3 개이다 위 코드는 함수가 실행될 때 아래와 같이 된다
function addAndPrint (a, b) {
console.log(a + b)
}
function foo (a) {
var a = 1
function addAndPrint (a, b) {
console.log(a + b, '개')
}
addAndPrint(a, 2) // 3 개
addAndPrint(a, 2) // 3 개
}
foo(1)
ㅎㅎ... 자바스크립트에서 함수를 만드는 방법은 총 2가지이다.
위 방법은 함수 선언식(Function Declarations)이다.
이 방법을 사용하면 위 처럼 foo가 실행될 때 함수 전체가 상단으로 끌어올려진다..
함수 선언식이 아닌 나머지 방법은 함수 표현식(Function Expressions)이라고 한다.
이 방법을 사용하면 함수 전체가 끌어올려지지 않는다 이 방법으로 예제를 바꿔보자.
function addAndPrint (a, b) {
console.log(a + b)
}
function foo (a) {
addAndPrint(a, 2) // 에러 발생(addAndPrint는 함수가 아님)
var addAndPrint = function (a, b) {
console.log(a + b, '개')
}
addAndPrint(a, 2)
}
foo(1)
위처럼 코드를 짜게 되면 아래 처럼 코드가 실행되어서 다행히 첫 함수 실행에 에러를 뱉어 미리 버그를 예측할 수 있다.
function addAndPrint (a, b) {
console.log(a + b)
}
function foo (a) {
var a = 1
var addAndPrint
addAndPrint(a, 2) // 에러 발생
addAndPrint = function (a, b) {
console.log(a + b, '개')
}
addAndPrint(a, 2)
}
foo(1)
다행히 es6에는 let, const라는 문법이 등장해 개발하는 데에 있어 훨씬 수월하다.
하지만 여기서 매우 중요한 점 let, const는 호이스팅이 발생하지 않는게 아니다.
let, const라고 해서 실행 컨텍스트와 그 안에 있는 렉시컬 환경을 사용하지 않는 게 아니다.
즉, let과 const는 초기화 전에 접근하면 에러를 뱉을 수 있도록 도와주는 것이지 호이스팅이 발생하지 않는 것이 아니다.
이렇게 호이스팅이 발생하는 배경에 대해 알아보고 호이스팅으로 인해 발생할 수 있는 버그에 대해 적어보았습니다.
다음 글은 클로저에 대해 다뤄보려고 합니다. 읽어 주셔서 감사합니다..!
'front-end > javascript' 카테고리의 다른 글
실행 컨텍스트를 자세히 알아보자 (2) | 2023.01.09 |
---|