본문 바로가기
[ Programming ] Basic/React

[리액트(React) 학습자를 위한 기초지식] var, let, const 차이점 - #2 hoisting, 적용범위

by the_little_coder 2020. 2. 2.

리액트(React) 학습에 필요한 사전지식

var, let, const 차이점 - #2 Hoisting, 적용범위



Hoisting


먼저 hoisting의 동사격이 되는 hoist의 사전적 의미는 이렇습니다.



to lift something heavy, sometimes using ropes or a machine
- Cambridge Dictionary

hoist는 "줄과 기계를 이용해 무거운 것을 들어 올린다."는 뜻을 가지고 있습니다. 구글에 hoist라고 검색하면 특정 장비 사진이 나옵니다. 그 장비로 hoist 단어의 이미지를 생각하시면 될 것 같습니다.


hoist는 이렇듯 "무언가를 들어 올리다."라는 뜻을 가지고 있습니다. 그렇다면 hoist는 우리가 코딩한 무언가를 올린다는 건데, 도대체 무엇을 들어올리는 걸까요? 예시를 통해 설명하겠습니다.



var의 hoisting


function getFoo(){
console.log(foo); //undefined
var foo = 100;
console.log(foo); //100
}

getFoo();



function getFoo(){...}로 함수 '선언'을 했고, getFoo()로 함수를 실행하도록 작성했습니다. 선언한 함수 function getFoo(){...}를 볼까요? 함수 첫줄에 foo라는 변수를 콘솔에 변수값이 나오도록 작성했습니다. 하지만 변수를 먼저 설정하지 않고 콘솔로그를 찍어서 "foo라는 변수를 정의하지 않았습니다." 라는 에러메세지가 나올 것 같지만, 에러는 발생하지 않았습니다. 보통 다른 언어는 에러메세지가 뜨는데... 이상하지 않나요?


자바스크립트에서 var는 변수에 값을 넣는 것, 즉 '할당'하는 것과 동시에 '선언'이 됩니다. 코딩에서 '할당'은 값을 넣어주는 것이고, '선언'은 생성 및 생성한 내용이라 할 수 있겠습니다.




//var는 이렇게 굳이 하지 선언, 할당 안 나눠도
var foo; //선언(Declaration)
foo = 100; //할당(Assignment)

//선언+할당 동시에 가능
var foo = 100;



자바스크립트는 엔진 구동시 '선언'문을 최우선적으로 해석합니다. 그리고 '할당' 구문은 런타임(Run Time, 실행) 과정에서 이루어집니다. 그렇단 얘기는 getFoo()함수 내에서   var foo = 100;  라고 선언과 할당을 동시에 했지만, 여기에서 '선언'만 함수 내 최우선적으로 해석한다는 겁니다. 이 내용을 다시 hoisting으로 적용하자면, var로 '선언+할당'구문을 동시에 작성해도 '선언'만 자바스크립트가 집어서 최상단으로 끌어 올려 먼저 해석(hoisting)한다는 겁니다. 작동 순서에 맞게 코드를 재구성하면 이렇습니다.



var의 hoisting 재구성


function getFoo(){
var foo; //선언이 hoisting 됨
console.log(foo); //undefined
foo = 100; //할당은 그 자리에...
console.log(foo); //100
}



그렇다면 let과 const는 과연 어떨까요?



let의 hoisting 도전(?)


function getFoo(){
console.log(foo);
let foo = 100;
console.log(foo);
}

getFoo();
//Uncaught ReferenceError: Cannot access 'foo' before initialization.



const의 hosting 도전(?)


function getFoo(){
console.log(foo);
const foo = 100;
console.log(foo);
}

getFoo();
//Uncaught ReferenceError: Cannot access 'foo' before initialization.



결과는 대실패. let, const는 선언+할당이 동시에 되지 않아 hosting이 적용되지 않는다는 걸 확인했습니다. 그렇다면 let과 const는 선언+할당을 따로 하면 잘 될까요?



let의 선언문 분리


function getFoo(){
console.log(foo);
let foo;
foo = 100;
console.log(foo);
}

getFoo();
//Uncaught ReferenceError: Cannot access 'foo' before initialization.



와 분리해도 안되네요. 레퍼런스에러, 즉 'foo'가 정의되지 않아 발생한 에러입니다. let은 선언문을 분리해도 최상단으로 hosting되지 않습니다. 변수를 (직접 작성하여) 먼저 선언하고, 그 이후에 변수를 사용해야 합니다. 그래야 에러가 발생하지 않고 작동합니다. const도 동일할까요? 미리 말씀드리자면 const는 선언+할당을 동시에 해야 합니다. 따로 해주게 되면 "Uncaught SyntaxError: Missing initializer in const declaration." 라고 에러메세지가 뜹니다.



const 정의


function getFoo(){
//console.log(foo);
//Uncaught ReferenceError: Cannot access 'foo' before initialization
const foo = 100;
console.log(foo);
}



let을 결국 최상단으로


function getFoo(){
let foo;
console.log(foo); //undefined
foo = 100;
console.log(foo); //100
}

getFoo();

 


변수를 생성할 때 var만 hoisting이 적용되는 걸 확인했습니다. 그렇다면 함수의 경우는 어떨까요? 함수도 '선언'을 하는데 말이죠.



함수선언 hoisting


console.log("1");

getFoo();

console.log("2");
console.log("3");

function getFoo(){
console.log('getFoo()');
console.log(foo); //undefined
var foo = 100;
console.log(foo); //100
}


/*

실행결과
1
getFoo()
undefined
100
2
3

*/



함수 선언부를 함수 실행구문 이후에 배치했는데도 잘 실행이 됩니다. 즉, 함수 선언부는 위치에 상관없이 해당 전체 코딩의 최상단부로 hoisting 됩니다. 그렇다면 함수 선언부를 변수에 할당하는 건 hoisting이 될까요?




console.log("1"); // 1

console.log('bar', bar); // bar undefined
getFoo();
//Uncaught ReferenceError: getFoo is not defined
//이후로 console에 log 출력 안 됨

console.log("2");
console.log("3");

var bar = function getFoo(){
console.log('getFoo()');
console.log(foo);
var foo = 100;
console.log(foo);
}



var bar = function getFoo(){  에서  var bar  선언부만 hosting되고 나머지는 작동하지 않는다는 걸 알 수 있습니다.



적용범위 - Scope


미리 얘기하자면 var는 함수범위(function-scoped)이고, let과 const는 블록범위(block-scoped)입니다. 


var의 적용범위


function func(){
if(true){
var foobar = 'foobar';
}
console.log(foobar); //foobar
}

func();


 


var를 let과 const로 바꿔볼까요?



function func(){
if(true){
let foobar = 'foobar';
}
console.log(foobar);
//Uncaught ReferenceError: foobar is not defined
}

func();




function func(){
if(true){
const foobar = 'foobar';
}
console.log(foobar);
//Uncaught ReferenceError: foobar is not defined
}

func();



let과 const는 함수범위가 아니라 block범위이기 때문에 적용이 되지 않습니다. block은 '{' 과 '}'로 이루어진 중괄호 내부를 의미합니다. 위 예시에서 foobar를 정의한 block과 foobar를 불러오는 block이 다릅니다. let과 const는 이렇듯 블록범위이기 때문에 var와는 다르게 함수적용이 되지 않습니다. 다른 예시를 들어볼게요.




var bar = 1;
let bas = 2;
const bat = 3;

function func(){
if(true){
let foobar1 = '100';
const foobar2 = '200';

console.log('in block', foobar1); //in block 100
console.log('in block', foobar2); //in block 200
}
console.log('out of block', foobar1); //Uncaught ReferenceError: foobar1 is not defined
console.log('out of block', foobar2); //Uncaught ReferenceError: foobar2 is not defined
}

func();



같은 블록 안에서 let과 const를 정의하고 불러오면 문제가 없이 잘 실행 됩니다. 하지만 해당 블록 밖에서 불러오면 "not defined"라고 에러가 발생합니다. 이것이 var, let, const의 범위입니다.


함수, 블록을 떠나서 전역으로 var, let, const를 설정할 수 있습니다. 위 예시를 보면 전역으로 var, let, const를 선언과 동시에 할당을 했습니다. 제가 위에서 "let은 hoisting이 적용되지 않는다."라고 적었습니다. 하지만 block범위에서는 선언+할당이 동시에 되어 hoisting이 적용됩니다. 위에서 let이 hoisting이 적용되지 않은 건 함수범위 내에서 시도하였기 때문입니다. 그럼 전역범위에서는 var, let const가 모두 hoisting이 되는지 확인해볼까요.




var bar;
let bas;
const bat; //Uncaught SyntaxError: Missing initializer in const declaration

bar = 1;
bas = 2;
const bat = 3;



전역범위에서도 역시 const는 선언이 분리가 되지 않는다는 걸 확인했습니다.



정리


1. 자바스크립트 엔진은 '선언'을 최우선적으로 해석.

2. hoisting은 var, let, const 선언부를, 함수에서도 선언부를 먼저 해석하는 것. '선언+할당'이 적용되는 경우, '선언'부를 자동으로 hoisting함.

3. var는 전역범위, 함수범위에서는 선언+할당 가능. let은 전역범위, 블록범위 내에서 선언+할당 가능. 함수범위에서는 불가. const는 어떤 범위에서든 선언+할당만 해야 함.


댓글