JavaScript - var, let, const, scope, hoisting

Sangho Moon·2020년 7월 9일
0

JavaScript

목록 보기
9/25
post-thumbnail

인터넷 강의를 듣는데 강사님이 변수 선언을 할 때마다 var를 쓰셨다. 그래도 어디서 주워들은건 있어서 복습할 때 그것들을 무심코 const로 바꾸며 진행하던 중 한 에러를 만났다. 에러를 복사 붙여넣기하며 구글링을 하다가 원인은 내가 무심코 바꿨던 const였다는 것을 깨달았다. 그래서 이번 계기로 var, let, const, 더 나아가 scope와 hoisting에 대한 개념을 확실히 잡고 가야겠다는 생각이 들었다.

1. var

var fruits = "apple";
console.log(fruits); // apple

fruits = "banana";
console.log(fruits); // banana

var fruits = "grape";
console.log(fruits); // grape

변수를 한 번 선언한 이후에도 위 코드처럼 값의 재할당 및 재선언이 가능하다.


2. let

let fruits = "apple";
console.log(fruits); // apple

fruits = "banana";
console.log(fruits); // banana

let fruits = "grape";
console.log(fruits); // Uncaught SyntaxError: Identifier 'fruits' has already been declared

변수의 값은 재할당이 가능하지만, 변수명은 재선언이 불가능하다.


3. const

const fruits = "apple";
console.log(fruits); // apple

fruits = "banana";
console.log(fruits); // Uncaught TypeError: Assignment to constant variable.

const fruits = "grape";
console.log(fruits); // Identifier 'fruits' has already been declared

상수를 한 번 선언하면 이후 그 값을 재할당하거나 재선언하는 것이 불가능하다.
그런데 관련 책을 보던 중 '변수를 재할당할 수는 없지만, 값을 바꿀수는 있다.' 는 구절이 있어서 console 창에 간단한 코드를 작성해보았다.


const arr = [];

for (i = 0; i < 3; i++) {
    arr.push(i);
}

console.log(arr); // [0, 1, 2]

이처럼 배열의 경우 값을 추가할 수 있었다.


이어서 다음과 같은 코드를 작성해 보았다.

const count = 10;
function canIBuy() {
    count = -1;
    if (count > 0) {
        console.log("you can buy it");
    } else {
        console.log("you can't buy it");
    }
}

canIBuy(); 

// Uncaught TypeError: Assignment to constant variable.
    // at canIBuy (<anonymous>:3:11)
    // at <anonymous>:11:1

상수 count를 10으로 선언한 뒤 canIbuy 함수 안에서 값을 변경하려고 시도해 보았다. const의 재할당 불가 특성상 당연히 에러가 나는 것이라고 생각했다. 그리고 다시 다음과 같이 함수 안의 count 앞에 const를 붙여서 실행해 보았다.

const count = 10;
function canIBuy() {
    const count = -1;
    if (count > 0) {
        console.log("you can buy it");
    } else {
        console.log("you can't buy it");
    }
}

canIBuy(); // you can't buy it

??? count의 값이 -1로 바뀌었다.
여기서 혼란스러워져서 친구에게 질문을 했더니 scope와 hoisting에 대해 검색해보라는 답을 얻었다.


4. scope

  • var는 함수 스코프를 갖는다.
  • let, const는 블록 레벨 스코프를 갖는다.

예제 코드를 보면,

function house() {
    let mouse_hole = true;
    if (mouse_hole) {
        
        const mouse_1 = '서울쥐';
        let mouse_2 = '시골쥐';
        var mouse_3 = '생떽쥐';
    }
    console.log(mouse_3);
}

house(); // 생떽쥐

var 로 선언한 생떽쥐는 if 문 안에서 선언되었더라도, if 문 밖 함수 house( ) 안에서 실행하면 문제없이 출력된다.

하지만 const나 let으로 선언한 서울쥐와 시골쥐를 같은 위치에 console.log로 입력해보면 다음과 같은 에러가 뜬다.

Uncaught ReferenceError: mouse_1 is not defined
    at house (<anonymous>:9:17)
    at <anonymous>:14:1

Uncaught ReferenceError: mouse_2 is not defined
    at house (<anonymous>:9:17)
    at <anonymous>:13:1

즉, var는 if 문 안에서 선언되었더라도 실행한 곳이 함수 안이기만 하면 실행이 되고, const나 let으로 선언한 변수는 if문 안에서만 실행이 되는 것을 알 수 있다.

간단히 var, let, const 별로 실행 제한범위가 정해져 있다고 생각하면 될 것 같다.

이 개념을 알고나서 위의 3. const 에서 count의 값이 왜 -1로 적용이 되는지 깨달았다.
함수 canIBuy( ) 안의 count는 밖에서 선언된 count와 별개로 함수 canIBuy( ) 안에서만 적용이 가능한 블록 레벨 스코프의 특성을 가진 상수라는 것을 알게 되었다.


5. hoisting

  • 선언문을 유효 범위의 최상단으로 끌어올리는 행위
  • 선언과 할당의 분리
console.log(mouse_1); // Uncaught ReferenceError

console.log(mouse_2); // Uncaught ReferenceError

console.log(mouse_3); // Undefined

const mouse_1 = '서울쥐';
let mouse_2 = '시골쥐';
var mouse_3 = '생떽쥐';

위 코드는 const, let, var로 각각 변수를 선언하기 전에 console.log로 실행시켰을 때의 결과를 보여준다.

생떽쥐의 코드만 보면 컴퓨터는 다음과 같이 해석한다.

var mouse_3;
console.log(mouse_3);
mouse_3 = '생떽쥐';

이것이 선언과 할당의 분리이다.
즉, var는 선언만 끌어올려질 뿐 값은 들어가기 전의 상태라 에러가 뜨지 않고 Undefined가 출력된다.

그럼 여기서 const나 let으로 선언한 서울쥐와 시골쥐는 호이스팅이 되지 않는다고 생각할 수 있다.

하지만 결론적으로 const와 let도 hoisting을 한다.

자세한 예시들은 참고 링크에 올려두겠다.


Ref.
책. 자바스크립트 코딩의 기술_길벗
https://www.youtube.com/watch?v=HsJ4oy_jBx0
https://gist.github.com/LeoHeo/7c2a2a6dbcf80becaaa1e61e90091e5d
https://velog.io/@bathingape/JavaScript-var-let-const-%EC%B0%A8%EC%9D%B4%EC%A0%90
https://medium.com/sjk5766/var-let-const-%ED%8A%B9%EC%A7%95-%EB%B0%8F-scope-335a078cec04
https://github.com/yalcodic/code-examples/blob/master/26_SCOPE/README.md

profile
Front-end developer

0개의 댓글