Jiyong's STUDY

Javascript의 Hoisting과 Scope 본문

Web/자바스크립트

Javascript의 Hoisting과 Scope

Kingjiyong 2021. 6. 28. 15:55
test1();
function test1() {
    console.log('test1');
}

--

node test.js
test1

--

test1의 선언보다 호출을 먼저 했지만 정상적으로 실행이 된다.

test2();
var test2 = function() {
    console.log('test2');
}

--

node test.js

/Users/jiyong/WebstormProjects/study-types/test.js:6
test2();
^

TypeError: test2 is not a function

--

마찬가지로 선언보다 호출을 먼저 했지만 오류가 난다.

 

두 개의 차이는 일반 함수와 인라인 함수에서 나는 차이긴 하지만, 아무튼 호이스팅이 적용된 결과라고 볼 수 있다.

 

test2();
let test2 = function() {
    console.log('test2');
}

--

node test.js

/Users/jiyong/WebstormProjects/study-types/test.js:6
test2();
^

ReferenceError: Cannot access 'test2' before initialization

--

var를 let으로 변경했을 때의 결과다.

 

TypeError -> ReferenceError로 에러의 타입이 변경되었다.

var로 선언했을 때에는 test2의 타입이 function이 아니라는 대답이 돌아왔고, 

let으로 선언했을 때에는 test2가 이니셜라이징 되기 전에 접근할 수 없다는 대답이 돌아온다.

 

이 차이를 제대로 확인해보자.

 

debugger;은 클라이언트에서 javascript 디버깅을 할 수 있게 해주는 키워드다.

편의를 위해 a로 시작하도록 만들었음.
var과 let은 스코프의 위치가 다름

 

a1의 경우에는 함수이고, a2는 var에 함수를 값으로 저장한 것이고, let도 함수를 값으로 저장한 것이다.

즉 a1은 일반 함수, a2와 a3은 인라인 함수이다.

 

이제 호이스팅이 뭔지 감을 잡을 수 있다. 호이스팅은 선언이 최상단으로 끌어올려지는 것을 말한다.

num = 10;
console.log(num);
var num;

--

node test.js
10

--​

이런 식으로 num을 늦게 선언했지만 자동으로 끌어올려진다.

하지만 이런 식으로 코드를 작성하면 문제가 생길 수 있다.

num += 10;
console.log(num);
var num = 0;

--

node test.js
NaN

--​​

결과는 NaN이 나왔다.

 

var num = 0;으로 초기화를 진행했고, 거기에 10을 더한 것인데 왜 10이 아니고 NaN이 나올까?

위에서 디버깅을 할 때 var 변수는 undefined로 보이는데 그렇기 때문일까?

undefined + 10 = NaN이다. 아마 그렇기 때문일거다. 자세히 보면,

a: undefined로 나와있다.

a를 선언할 때 var a = 0;으로 했지만 0이 아닌데,

var a = 0;이라는 구문이 실행되어야만 a가 초기화가 된다.

즉 선언만 끌어올리는 것이지 초기화도 같이 하는 것은 아니라는 것이다.

정확히 저 두 줄의 코드는 다음과 같이 실행된다.

var num;
console.log(a);
a = 0;

 

또한 위에 함수 부분 디버깅에서 let이 Script로 나와 있는 이유는 블록 레벨 스코프가 적용되어 있기 때문이다.

기존의 var은 비 블록 레벨 스코프가 적용되어 있는데 그 차이를 보면,

 

if (true) {
    var a = 1;
    let b = 2;
}

console.log(a);
console.log(b);

--

node test.js 
1
/Users/jiyong/WebstormProjects/study-types/test.js:7
console.log(b);
            ^

ReferenceError: b is not defined

--

1은 출력되었다. 즉 a는 접근이 가능했다는 것이다.

b의 경우에는 접근이 불가능했는데, 에러를 확인하면 ReferenceError로 not defined가 되어있다.

 

블록 레벨 스코프는 쉽게 {} 단위로 짤린다는 이야기인데,

let의 경우에는 if문으로 묶인 부분에 블록 레벨이 적용되어 그 블록 안에서만 사용되고 그 밖에서는 사용이 불가능하다.

다만 var는 블록 밖에서도 사용이 가능했다.

 

다시 돌아가서, 블록 범위에서의 호이스팅을 알아보면

console.log('before a: %d', a);

if (true) {
    var a = 1;
    let b = 2;
}

console.log(a);
console.log(b);

--

node test.js
before a: NaN
1
/Users/jiyong/WebstormProjects/study-types/test.js:9
console.log(b);
            ^

ReferenceError: b is not defined

--​

var a는 전체 범위에서 호이스팅이 되는 것이 아니라 블록 단위로 호이스팅이 되는 것을 알 수 있다.

그렇다는 이야기는 var은 그냥 블록의 최상단에 호이스팅이 되는 것이다.

왜냐? 자바스크립트의 코드는 하나의 함수로 취급이 되고, 그 블록 스코프에서 맨 위로 당겨지기 때문에

전체 블록의 최상단에 호이스팅이 된다고 생각해도 된다.

'Web > 자바스크립트' 카테고리의 다른 글

Javascript의 call, apply, bind  (0) 2021.06.29
Javascript의 this와 call  (0) 2021.06.28
클로저(Closure)란?  (0) 2021.06.24
Javascript의 실행 컨텍스트  (0) 2021.05.26
Javascript의 함수  (0) 2021.05.26