자바스크립트에서 변수를 선언할 때 var, let, const 형태로 선언할 수 있습니다.
var는 function-scoped라고 함
let, const는 block-scoped라고 함
여기서 function-scoped와 block-scoped가 무엇일까 알아보니 다음과 같은 내용을 볼 수 있었습니다.
01. var (function-scoped)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
for ( var j=0; j<10; j++) {
console.log( 'j' , j)
}
console.log( 'after loop j is ' , j)
function counter () {
for ( var i=0; i<10; i++) {
console.log( 'i' , i)
}
}
counter()
console.log( 'after loop i is' , i)
|
여기서 hoisting이라는 말이 나오는데.. (처음에 hosting이라고 착각함 ㅎㅎ....)
이건 또 무슨 말이고 하니.. 요약하자면 "후선언된 변수나, 함수들이 해당 Scope에서 최상위에 위치하는 걸 뜻함"이라고 합니다.
대충 선언 부분이 잘못되도 자바스크립트에서는 이를 올바르게 코딩된 것처럼 인식한다는 뜻인데, 이는 다음 포스트에서 다루도록 하겠습니다.
위에서 나타난 에러처럼 ReferenceError 즉, 선언이 안 되었다는 에러가 나타납니다.
간단히 이해하기 위해 다음과 같이 코드를 바꿀 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var j=0;
for (j=0; j<10; j++) {
console.log( 'j' , j)
}
console.log( 'after loop j is ' , j)
function counter () {
var i=0;
for (i=0; i<10; i++) {
console.log( 'i' , i)
}
}
counter()
console.log( 'after loop i is' , i)
|
위와 같이 지역변수의 느낌으로 이해하면 쉽게 와닿을 수 있습니다.
자바스크립트에서는 immediately-invoked function expression( or IIFE, pronounced "iffy")라는 것이 있다고 합니다..
무슨 말인지 알아먹기 힘든 영어로 되어있지만 예시를 통해 알아보도록 합시다.
다음은 IIFE로 function-scope처럼 만들 수 있는 예시입니다.
IIFE function-scope 예시(1)
1 2 3 4 5 6 7 8 9 |
( function () {
for ( var i=0; i<10; i++) {
console.log( 'i' , i)
}
})()
console.log( 'after loop i is' , i)
|
위의 소스코드를 보면 소괄호로 묶어 마치 하나의 함수처럼 만들어준 것처럼 보입니다.
hoisting이 되는 위치도 웃긴 게, function 내부에 되어버리게 되어, 결국 function 밖에서 실행할 때 사용되는 i의 값은 undefined 변수가 되어버리게 됩니다.
에러가 나지 않도록 작성한 코드는 다음과 같습니다.
IIFE function-scope 예시(2) - hoisting
1 2 3 4 5 6 7 |
( function () {
for (i=0; i<10; i++) {
console.log( 'i' , i)
}
})()
console.log( 'after loop i is' , i)
|
위의 소스코드가 에러가 나지 않는 이뉴는 i를 function 내에서 다시 재정의하지 않아서 hoisting이 이루어졌다는 의미입니다.
즉, i 함수는 function 밖에서 선언된 전역(global) 변수라는 것입니다.
이러한 hoisting을 방지하기 위해서는 use strict라는 것을 사용할 수 있습니다.
밖에서 사용한 전역변수가 함수 내에서 hoisting 되지 않도록 방지하는 것입니다.
IIFE function-scope 예시(2) - use strict
1 2 3 4 5 6 7 8 9 |
var i;
( function () {
'use strict'
for (i=0; i<10; i++) {
console.log( 'i' , i)
}
})()
console.log( 'after loop i is' , i)
|
위의 소스코드는 밖에서 선언된 var i의 값이 hoisting되지 않았기 때문에, i의 값에는 아무런 값이 들어가 있지 않습니다.
따라서 정상적으로 에러를 출력하는 걸 볼 수 있습니다.
변수 하나에 너무나 많은 고민이 들어가게 됩니다. 허허...
이러한 수고를 덜어주기 위해 등장한 것이 바로 let과 const라고 할 수 있습니다.
02. let, const (block-scoped)
자바스크립트에는 var만 존재했을 때 다음과 같은 문제가 있었다고 합니다.
1 2 3 4 5 6 7 |
var a = 'test'
var a = 'test2'
c = 'test'
var c
|
위와 같은 문제점으로 인해 자바스크립트를 욕하는 사람이 많았다고 합니다.. ㅎㅎ
하지만 let과 const를 사용하면 var를 사용할 때보다 훨씬 편리하게 코드를 작성할 수 있게 되었다고 합니다.
이 두 가지 형태는 변수의 재선언이 불가능하다는 공통점이 있습니다.(마치 C언어의 #define과 const의 느낌이 아닐까 싶습니다.)
let은 변수에 재할당이 가능하지만, const는 재선언, 재할당이 모두 불가능합니다.
1 2 3 4 5 6 7 8 9 |
let a = 'test'
let a = 'test2'
a = 'test3'
const b = 'test'
const b = 'test2'
b = 'test3'
|
이때 let과 const에 hoisting이 발생하지 않는 건 아닙니다.
만약 var가 function-scoped로 hoisting 되었다면 let과 const는 block-scoped로 hoisting이 일어나게 됩니다.
1 2 3 4 5 6 7 8 9 10 |
d = "test"
var d;
c = 'test'
let c;
|
c가 에러가 나는 이유는 let의 특징인 tdz(temporal dead zone) 때문입니다.
이는 let으로 선언된 변수는 값을 할당하기 전에 변수가 먼저 선언되어 있어야 합니다.(위의 4 ~ 6번째 라인과 같은 hoisting이 일어나지 않음)
이는 const도 예외가 아닙니다. 단지 let보다 좀 더 업격할 뿐...
1 2 3 4 5 6 |
let dd
dd = 'test'
const aa
|
자바스크립트에 tdz와 같은 기능이 필요한 이유는 동적언어이다보니 runtime type check가 필요해서라고 합니다.