자바스크립트 엔진은 변수를 3단계로 나눠서 인지한다.
변수선언 3단계는 선언단계
, 초기화단계
, 할당단계
로 이루어진다.
📍선언단계
(var, let, const) 로 변수를 선언하는 단계, 야 이거 변수야! 라고 엔진한테 알려주는 것
📍초기화단계
자바스크립트 엔진은 그 코드가 마치 전체 코드의 선두인것처럼 끌어올려 그 값을 저장할 메모리 공간을 따로 확보한다.
이를 변수 호이스팅이라 한다. var
의 경우 선언 시 초기화단계를 암묵적으로 undefined
로 실행하고 let
과 const
는 변수만 저장해놓고 선언하기 전까지 아래에 나올 개념인 TDZ
에 넣어둔다.
📍할당단계
할당단계는 앞에서 선언했던 변수에 값을 할당하는 단계이며 선언과 할당을 한 줄로 할 수 있다. 한 줄로 할 경우 초기화된 공간에 메모리를 넣는 것이 아니라 초기화된 공간을 없애고 새로운 공간에 메모리를 넣는다고 함. 더이상 메모리를 사용할 필요가 없으면 Null을 할당한다.
JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.
💡 호이스팅은 자바스크립트 엔진이 작업 실행 전에 변수와 함수만 골라 따로 모아두는 것!
var
로 선언한 변수의 경우 호이스팅 시 undefined
로 변수를 초기화하는 반면 let
과 const
로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않고 선언만 가져간다.
var
는 재선언 남발이 가능해서 암묵적으로 초기화해놓는 것이다.
호이스팅을 구체적으로 나눠서 표현하자면, var
는 "선언과 초기화를 세트"로 가져가고, let
과 const
는 "변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는 것"이다. 따라서 변수를 정의하는 코드보다 변수를 사용하는 코드가 먼저 등장할 수 있다. 다만 선언과 초기화를 함께 수행하는 경우, 선언 코드까지 실행해야 변수가 초기화된 상태가 됨!
💡 변수랑 함수를 먼저 따로 모아놨다가 변수를 사용하는 코드가 나타나면 변수 선언이 앞 줄에 있든 뒷 줄에 있든 상관없이 변수를 꺼내서 적용해준다. 선언 전에 변수를 사용할 경우
var
는undefined
를 꺼내고 나머지는ReferenceError
가 뜬다.
JS 엔진 : 나중에 값이 어떻게 할당될지 아직 모르겠고, 일단 젤 첨에 변수 선언한 애들 모아놓을게!
var
선언 시에는 undefined
뜸.
console.log(num); // 호이스팅한 var 선언으로 인해 undefined 출력
var num; // 선언
num = 6; // 초기화
다음 예제는 선언 없이 초기화만 존재하기 때문에 호이스팅에서 제외되고, 변수를 읽으려고 하면 에러가 뜸.
console.log(num); // ReferenceError
num = 6; // 초기화
아래 코드를 살펴보자.
// 예제 1
// y만 호이스팅 대상
x = 1; // x 초기화. 그러나 명령문에 var가 없으므로 호이스팅이 발생하지 않음
console.log(x + " " + y); // '1 undefined'
// JavaScript는 선언만 호이스팅하므로, 윗줄의 y는 undefined
var y = 2; // y를 선언하고 초기화
// 예제 2
// 호이스팅은 없지만, 변수를 사용할 수 있음
a = '크랜'; // a 초기화
b = '베리'; // b 초기화
console.log(a + "" + b); // '크랜베리'
💡
let
과const
호이스팅
let과 const로 선언한 변수도 호이스팅 대상이지만, var와 달리 호이스팅 시 undefined로 변수를 초기화하지는 않습니다. 따라서 변수의 초기화를 수행하기 전에 읽는 코드가 먼저 나타나면 ReferenceError가 발생합니다.
TDZ은 let
, const
, class
구문의 유효성을 관리한다.
자바스크립트에서 변수가 동작하는 방식을 이해하는 것은 매우 중요하다.
let
, const
, class
는 var
와 다르게 선언단계와 초기화 단계가 분리되어 있기 때문에
선언하기 전에 변수를 호출하면 참조 에러(ReferenceError)가 발생한다.
변수를 호이스팅은 하지만, 실제로 선언하는 코드가 나타나기 전까지 내보내지는 않는 것!
TDZ
는 변수가 호이스팅되어 선언되기 전까지 머무는 사각지대 라고 이해하면 쉽다.
참고로 function함수는 변수선언 3단계를 동시에 진행해버린다.
1. const
const
먼저 살펴보자.
const white = '#FFFFFF'; // 변수를 선언하고나서
white; // => '#FFFFFF' // 초기화하면 변수에 접근할 수 있다. (정상동작)
이번에는 선언 전에 변수를 부르면 어떻게 되는지 보자.
white; // throws `ReferenceError`
const white = '#FFFFFF';
white
💡 이 때
const white = '#FFFFFF'
구문 전까지, white 변수는 TDZ에 있다!
TDZ에 있는 white 변수에 접근하게 되면 , ReferenceError: Cannot access 'white' before initialization 자바스크립트 에러가 발생한다.
2. let
let
도 const
와 마찬가지다.
// Does not work!
count; // throws `ReferenceError`
let count;
count = 10;
선언 이후에 사용해야 한다.
let count;
// Works!
count; // => undefined
count = 10;
// Works!
count; // => 10
3. class
class
또한 선언 전에는 사용할 수 없다.
// Does not work!
const myNissan = new Car('red'); // throws `ReferenceError`
class Car {
constructor(color) {
this.color = color;
}
}
이렇게 써야 정상작동!
class Car {
constructor(color) {
this.color = color;
}
}
// Works!
const myNissan = new Car('red');
myNissan.color; // => 'red'
//함수 선언문
function getName() {
console.log('name');
}
//함수 표현식
var name = function() {
console.log('name');
};
javascript 에서는 함수를 변수에 담을 수 있다. 이렇게 사용하는 것을 함수 표현식
이라고 한다.
그리고 function getName() 과 같이 함수를 선언하는 것을 함수 선언문
이라고 한다.
그렇다면 함수 표현식과 함수 선언문 둘다 함수이지만 호이스팅에 차이가 있다.
함수 선언문
은 해당 함수 전체 내용을 호이스팅한다. (var
초깃값 undefined
세트로 저장하는 것처럼)함수 표현식
은 선언만 저장하고 값은 호이스팅하지 않는다. (let
또는 const
처럼)부등호가 큰 쪽이 먼저 인식된다
변수 선언 > 함수 선언
할당되어있는 변수 > 할당되지 않은 변수
참고자료 출처 - hyun-jii.log
참고자료 출처2 - ui.toast.com
참고자료 출처3 - developer.mozilla.org