자바스크립트 단골 면접 질문 - 호이스팅, var vs let vs const에 대해서 아는 데로 설명해주시겠어요?
구글 영어사전에 따르면 호이스팅이란 밧줄과 도르래로 끌어올리다는 뜻.
: raise (something) by means of ropes and pulleys.
"high overhead great cranes hoisted girders"
Simple Korean으로 말하면, 이사짐 사다리를 사용해서 63빌딩 꼭대기 층으로 위로 올라가는 것.
aCatName("호랑이");
function aCatName(name) {
console.log("고양이의 이름은 " + name + "입니다");
// 고양이의 이름은 호랑이입니다
}
일반적으로 코드는 탑다운 방식으로 실행이 된다.
위의 예제를 보면 함수를 선언하기도 전에, 함수를 먼저 호출하는데도 문제가 없다. 콘솔이 잘 찍힌다.
debugger를 확인해보면, 실행도 전에 호이스팅을 하여 Global에 aCatName 함수가 담긴것을 볼 수 있다. 여기서 우리가 '아하'라고 생각돼야 하는 부분은, 함수가 실행도 전에 꼭대기 층으로 올라가서 어딘가에 저장된다는 것이다. 이게 호이스팅이다.
자연스럽게 우리에게 말도하지 않고, 63빌딩 꼭대기로 쭈욱 올라간다는 것.
그렇다. 호이스팅은 코드를 실행하기도 전에, 선언된 변수 및 함수를 해당 범위의 맨 위로 이동하는 JavaScript의 메커니즘이다.
함수 선언문이 아닌 함수 표현식과 화살표 함수는 호이스팅이 될까?
"The context inside arrow functions is lexically or statically defined. " -
몇 블로그 글을 보면... 누군가는 var는 호이스팅이 되는데 let과 const는 호이스팅이 되지 않는다는... 얘기를 한다. 까짓거 그럼 debugger를 사용해서, 자바스크립트 엔진에게 물어보자.
var 키워드
let, const 키워드
결론: var, let, const 코드를 실행하기도 전에, 선언된 변수 및 함수를 위로 쭈욱~~~
스코프(scope): 변수에 접근할 수 있는 범위
function getPassword() {
var secretKey = '12345';
}
getPassword();
console.log(secretKey); // ReferenceError - not defined
함수 외부는 Global scope이고, 함수 내부는
getPassword 함수
의 scope이다. 부모 scope는 자식 scope에서 간섭할 수 없기 때문에 접근이 불가능하다.
그렇다면 var 키워드를 사용할 때, 블락{ } 에서는?
// ========= 블락 {} =========
var secretKey = "123456";
if (true) {
var secretKey = "var changes inside block - Not safe";
}
console.log(secretKey); // var changes inside block - Not safe
대표적인 예로, for loop의 블락스코프다.
블락에서 보호를 제대로 받지 못하여, 다른 스코프(범위)에서 키워드를 남발할 수 있다.for (var password = 0; password < 2; password++){ console.log(password); // 0, 1 } console.log(password); // 2 // 블락스코프 밖에서... password를 선언하지도 않았는데, i에 접근해버렸다..
var 키워드를 남발한다면... 나도 모르게 변수를.. 재사용하여.. 이거저것 해본 후... 어디선가.. 버그를 찾고 있겠지...?
Simple Korean: 도둑이 이집 저집 마음대로 넘나들면 안되니까 벽을 세워야함.
이를 위해, 도둑이 넘나들지 못하게 벽을 세우자! ES6에서 블락스코프 let, const 키워드
가 등장했다.
let secretKey = "Let me hack your password";
if(true){
let secretKey = "Go away";
console.log(secretKey); // "Go away";
const password = "*******"
console.log(password); // "*******"
}
console.log(secretKey); // Let me hack your password
console.log(password); // ReferenceError - password is not defined
let과 const를 사용하여 도둑이 집을 넘나드는것을 안전한 벽(블락)을 세우며 침입을 막는다.
앞서, 호이스팅를 확인하고 중요한 함수스코프와 블락스코프의 차이를 확인했다. 그렇다면 var, let, const 쓰임새는 어떻게 될까?
앞에서도 살펴봤지만, 다시 위의 사진을 보면, let과 const를 사용할때, 호이스팅이 돼서 메모리 어딘가에 undefined
로 먼저 들어가 있는 것을 확인할 수 있다. 이뿐만이 아니다, 개발자 도구에서 함수를 정의해도 undefined가 뜬다.
undefined
를 넣어두려고 시도한다.Simple Korean으로 설명을 하자면,
1. 변수 선언.
2. 초기화라는건 => 메모리 공간을 미리 만들어 둔다는 것이다. 보통 암묵적으로 'undefined'을 넣으려고함.
3. 초기화 단계에서 만들어 놓은 메모리 공간을 활용하여 => 값을 할당 및 재할당 한다.
undefined
로 넣어두고 다음 테스크를 기다린다.var whatever; // undefined
var whatever = '1' // 재선언 가능
var whatever = '2' // 재할당 가능
undefined
에서 나중에 재할당을 할 수 있다. 즉, 선언과 초기화 분리가 가능하다. let laterPossible; // undefined
laterPossible = "changing the value"
let laterPossible = 'declared again'; // 중복선언 안됨.
// Identifier 'laterPossible' has already been declared
const strictBetter; // Missing initializer in const declaration
// 1. 선언할때, 2. 초기화를 꼭 하고, 값도 할당해줘야한다.
const strictBetter = 'value in the memory' // 선언 & 초기화 & 값 할당
const strictBetter = 're-declare'; // 중복선언 안됨.
// Identifier 're-declare' has already been declared
strictBetter = 'new string'; // Assignment to constant variable
const exceptionConst = {id:1, name: 'Frank'}
exceptionConst['name'] = 'Frank Kim'
exceptionConst['job'] = 'JavaScript Developer'
console.log(exceptionConst);
// {id: 1, name: 'Frank Kim', job: 'JavaScript Developer'}
Temporal Dead Zone: => ReferenceError
(1) let
console.log(name); // Uncaught ReferenceError: name is not defined
/*
......
...... T
...... D
...... Z
......
*/
let name = 'Frank'
(2) const
const 키워드는 선언 단계와 초기화 단계가 동시에 진행된다.
console.log(name)
// Uncaught ReferenceError: Cannot access 'name' before initialization
const name = 'Frank'
let 키워드로 선언한 경우, 런타임 이전에 선언이 되어 자바스크립트 엔진에 이미 존재하지만 초기화가 적절하게 되지 않았기 때문에, name is not defined라는 문구가 떴다.
하지만 const 키워드로 선언한 경우, 선언과 초기화가 동시에 이루어져야 하지만 런타임 이전에는 실행될 수 없다. 따라서 초기화가 진행되지 않은 상태이기 때문에 Cannot access 'name' before initialization 에러 문구가 뜬다.
기본적으로 변수의 스코프는 최대한 좁게 만드는 것을 권장한다.
따라서, var > let > const 키워드를 사용하자.
변경하지 않는 값(상수)이라면 let > const 키워드를 사용하는 것이 상대적으로 안전한다.
Reference: