자밥스크립트에서 ES6가 출현하기 전에는 변수를 선언시 var 예약어 선언이 지배했습니다.
그러나 var로 선언된 변수와 관련된 문제가 있습니다.
그렇기 때문에 변수를 선언하는 새로운 방법이 등장해야 했습니다.
ES6 에서 변수선언에 사용되는 let, const 예약어를 배우기 전에 이전의 변수 선언의 문제점을 보도록 하겠습니다.
1. 중복 선언이 가능합니다.
|
// var를 이용하면 한번 정의된 변수를 재정의 할 수 있습니다.
var i = 1;
console.log(`i = ${i}`);
var i = 2; // 오류가 오류가 아닙니다.
console.log(`i = ${i}`);
|
2. 상수라고 대문자로 선언해도 변경이 가능합니다.
|
// 상수라고 대문자로 선언해도 재할당이 된다.
var PI = 3.141592;
console.log(`원주율 = ${PI}`);
PI = 3.14; // 오류가 오류가 아닙니다. 변경 가능합니다.
console.log(`원주율 = ${PI}`);
|
3. 변수 선언 전에 사용해도 오류가 아닙니다.
|
// 선언전에 사용해도 오류가 아닙니다.
console.log(`j = ${j}`); // j = undefined
var j = 10;
/*
위의 코드는 다음과 같이 처리 됩니다.
var j = undefined;
console.log(`j = ${j}`);
j=10;
*/
|
호이스팅(hoisting)
호이스팅은 코드가 실행하기 전 변수선언/함수선언이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말합니다.
자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정을 거치게 되는데 이때 모든 선언(var, let, const, function, class)을 스코프에 등록합니다.
위의 코드가 오류가 아닌것은 코드 실행 전 이미 변수선언/함수선언이 저장되어 있기 때문에 선언문보다 참조/호출이 먼저 나와도 오류 없이 동작하기 때문입니다.
정확히는 var 키워드로 선언한 변수와 함수 선언문일 경우 오류 없이 동작합니다. 이는 선언이 파일의 맨 위로 끌어 올려진 것 같은 현상인 호이스팅이 발생하기 때문입니다.
호이스팅에 관해서는 추후에 다시 한번 자세하게 포스칭 하겠습니다.
var_ex01.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>var의 문제점</title>
<script>
// 구분선을 그려주는 함수 선언
let line = () => console.log('-'.repeat(50));
// var를 이용하면 한번 정의된 변수를 재정의 할 수 있습니다.
var i = 1;
console.log(`i = ${i}`);
var i = 2; // 오류가 오류가 아닙니다.
console.log(`i = ${i}`);
line();
// 상수라고 대문자로 선언해도 재할당이 된다.
var PI = 3.141592;
console.log(`원주율 = ${PI}`);
PI = 3.14; // 오류가 오류가 아닙니다. 변경 가능합니다.
console.log(`원주율 = ${PI}`);
line();
// 선언전에 사용해도 오류가 아닙니다.
console.log(`j = ${j}`); // j = undefined
var j = 10;
line();
</script>
</head>
<body>
</body>
</html>
|
cs |
실행 결과 입니다.

4. 정의된 변수가 함수 스코드(Scope)를 가집니다.
var 키워드 없이 변수를 선언하면 전역 변수가 됩니다.
|
// var 키워드를 사용하지 않고 변수에 값을 할당하면 전역 변수가 됩니다.
k1 = 10; // 전역 변수
function fn1() {
k1 = 20; // 전역 변수
console.log(`함수 내부에서 k1 = ${k1}`);
}
console.log(`함수 외부에서 k1 = ${k1}`); // 10
fn1(); // 20
console.log(`함수 외부에서 k1 = ${k1}`); // 10
|
함수 내부에서 var없이 선언하면 전역 변수가 됩니다. 함수 호출 후에는 어디서든 사용이 가능합니다.
|
// 함수안에서 var 키워드를 사용하지 않고 변수에 값을 할당하면 전역 변수가 된다.
function fn2() {
k2 = 10; // 최초 호출 후 전역 사용 가능
console.log(`함수 내부에서 k2 = ${k2}`);
}
// console.log(`함수 외부에서 k2 = ${k2}`); // 호출전 : ReferenceError: k2 is not defined
fn2(); // 10
console.log(`함수 외부에서 k2 = ${k2}`); // 10
|
함수 내부에서 var키워드를 사용하면 함수 내부에서만 유효한 지역변수가 됩니다.
|
function fn3() {
var k3 = 10; // 지역 변수
console.log(`함수 내부에서 k3 = ${k3}`);
}
// console.log(`함수 외부에서 k3 = ${k3}`); // ReferenceError: k3 is not defined
fn3(); // 10
// console.log(`함수 외부에서 k3 = ${k3}`); // ReferenceError: k3 is not defined
|
다음과 같은 경우에는 함수안의 k4와 밖의 k4는 다른 변수 입니다.
|
var k4 = 10; // 전역변수
function fn4() {
var k4 = 20; // 지역 변수
console.log(`함수 내부에서 k4 = ${k4}`);
}
console.log(`함수 외부에서 k4 = ${k4}`); // 10
fn4(); // 20
console.log(`함수 외부에서 k4 = ${k4}`); // 10
|
다음의 코드는 결과가 어떻게 될까요?
|
var k5 = 10;
function fn5(){
console.log(`함수 내부에서 k5 = ${k5}`);
var k5 = 20;
console.log(`함수 내부에서 k5 = ${k5}`);
}
fn5();
|
생각 같아서는 다음과 같을 것 같지만 그렇지 않습니다.
| 예상 결과 함수 내부에서 k5 =10 함수 내부에서 k5 = 20 실제 결과 함수 내부에서 k5 =undefined 함수 내부에서 k5 = 20 |
Hoisting이란 var 키워드를 사용하여 변수를 선언 시, 해당 변수가 속한 범위(scope) 최상단으로 올려버리는 현상이라고 했습니다. 그리고 주목할 점은 여기서 속한 범위는 다른 언어처럼 block 레벨이 아니라 function 레벨이라는 점입니다. 함수레벨로 변수 Hoisting 현상이 발생하면 위 코드는 다음과 같이 해석되어 집니다.
|
var k5 = 10;
function fn5(){
var k5 = undefined; console.log(`함수 내부에서 k5 = ${k5}`);
k5 = 20;
console.log(`함수 내부에서 k5 = ${k5}`);
}
fn5();
|
그래서 실제 결과가 위의 것 처럼 나나나는 것 입니다.
var_ex02.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>var의 문제점</title>
<script>
// 구분선을 그려주는 함수 선언
let line = () => console.log('-'.repeat(50));
// 2. 정의된 변수가 함수 스코프(scope)를 가집니다.
// var 키워드를 사용하지 않고 변수에 값을 할당하면 전역 변수가 됩니다.
k1 = 10; // 전역 변수
function fn1() {
k1 = 20; // 전역 변수
console.log(`함수 내부에서 k1 = ${k1}`);
}
console.log(`함수 외부에서 k1 = ${k1}`); // 10
fn1(); // 20
console.log(`함수 외부에서 k1 = ${k1}`); // 10
line();
// 함수안에서 var 키워드를 사용하지 않고 변수에 값을 할당하면 전역 변수가 된다.
function fn2() {
k2 = 10; // 최초 호출 후 전역 사용 가능
console.log(`함수 내부에서 k2 = ${k2}`);
}
// console.log(`함수 외부에서 k2 = ${k2}`); // 호출전 : ReferenceError: k2 is not defined
fn2(); // 10
console.log(`함수 외부에서 k2 = ${k2}`); // 10
line();
function fn3() {
var k3 = 10; // 지역 변수
console.log(`함수 내부에서 k3 = ${k3}`);
}
// console.log(`함수 외부에서 k3 = ${k3}`); // ReferenceError: k3 is not defined
fn3(); // 10
// console.log(`함수 외부에서 k3 = ${k3}`); // ReferenceError: k3 is not defined
line();
// var 키워드를 사용하고 변수에 값을 할당하면 지역 변수가 된다.
var k4 = 10; // 전역변수
function fn4() {
var k4 = 20; // 지역 변수
console.log(`함수 내부에서 k4 = ${k4}`);
}
console.log(`함수 외부에서 k4 = ${k4}`); // 10
fn4(); // 20
console.log(`함수 외부에서 k4 = ${k4}`); // 10
line();
// 다음의 결과는 어떻게 될까요?
var k5 = 10;
function fn5(){
console.log(`함수 내부에서 k5 = ${k5}`);
var k5 = 20;
console.log(`함수 내부에서 k5 = ${k5}`);
}
fn5();
line();
</script>
</head>
<body>
</body>
</html>
|
cs |
실행 결과 입니다.

5. 반복문에서 정의된 변수가 반복문이 끝난 이후에도 계속 남아 있다.
for 반복문에서 var를 사용하면 더 당황스러운 경우를 겪을 수도 있습니다.
다음의 코드에서 변수 i의 값은 for 루프가 끝난 다음에도 유지가 됩니다. 왜냐하면 위에 설명드린 것 처럼 var로 선언한 변수는 if나 for와 같은 block 레벨 아닌 function 레벨에서 범위가 정해지기 때문입니다. 즉, for 문 안에서 var로 선언한 변수도 block을 벗어나서 유효하게 됩니다.
|
var sum=0;
for (var i = 1; i <= 10; i++) {
sum += i;
}
console.log(`합계 sum = ${sum}`); // 합계 sum = 55
console.log(`밖에서 i = ${i}`); // 밖에서 i = 11
|
이와 같은 문제점을 해결하기 위해서는 다음과 같이 즉시 실행함수를 사용하면 됩니다.
즉시 실행함수를 사용하면 변수를 함수 내부에 한정 시킬 수 있습니다.
|
var sum=0;
(function () {
for (var j = 1; j <= 10; j++) {
sum += j;
}
})();
console.log(`합계 sum = ${sum}`); // 합계 sum = 55
// 에러 발생 : ReferenceError: j is not defined
console.log(`밖에서 j = ${j}`);
|
var_ex03.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>var의 문제점</title>
<script>
// 구분선을 그려주는 함수 선언
let line = () => console.log('-'.repeat(50));
var sum=0;
for (var i = 1; i <= 10; i++) {
sum += i;
}
console.log(`합계 sum = ${sum}`);
console.log(`밖에서 i = ${i}`);
line();
// 위 문제를 해결하려면
var sum=0;
(function () {
for (var j = 1; j <= 10; j++) {
sum += j;
}
})();
console.log(`합계 sum = ${sum}`);
// 에러 발생 : ReferenceError: j is not defined
console.log(`밖에서 j = ${j}`);
line();
</script>
</head>
<body>
</body>
</html>
|
cs |
실행 결과 입니다.

다음의 경우에도 문제가 생깁니다.
var를 이용하여 반복문을 실행하여 이벤트를 지정하였습니다. 이때 출력되는 메세지는 호출 시점에서의 변수값이됩니다.
var_ex03.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>var의 문제점</title>
<style>
button {
width: 80px;height: 80px;margin: 10px;
}
</style>
<script>
window.onload = () =>{
for(var i=1;i<=3;i++){ // var로 선언
document.querySelector("#btn"+i).onclick = ()=>{
alert(`${i}번 버튼 눌림!!!`); // 호출 시점의 i값 출력
}
}
for(var i=4;i<=6;i++){ // var로 선언
// 클로저 사용
document.querySelector("#btn"+i).onclick = ((num)=>{
// 함수리턴
return ()=>{
alert(`${num}번 버튼 눌림!!!`); // 할당 시점의 i값 출력
}
})(i); // 직접실행함수
}
for(let i=7;i<=9;i++){ // let으로 선언
document.querySelector("#btn"+i).onclick = ()=>{
alert(`${i}번 버튼 눌림!!!`); // 할당 시점의 i값 출력
}
}
}
</script>
</head>
<body>
<button id="btn1">눌러줘 01</button>
<button id="btn2">눌러줘 02</button>
<button id="btn3">눌러줘 03</button>
<br>
<button id="btn4">눌러줘 04</button>
<button id="btn5">눌러줘 05</button>
<button id="btn6">눌러줘 06</button>
<br>
<button id="btn7">눌러줘 07</button>
<button id="btn8">눌러줘 08</button>
<button id="btn9">눌러줘 09</button>
</body>
</html>
|
cs |
실행 결과 입니다.

1~3번까지의 버튼을 누르면 실행시점의 i변수의 값이 적용되어 "7번 버튼이 눌림!!!"이란 경고창을 띄;우는 황당함을 볼 수 있는니다.

4 ~ 6번 버튼은 클로저를 이용하여 즉시 실행함수로 만들었습니다. 제대로 작동됩니다.

7~9번 버튼은 let 키워드로 변수를 선언해서 의도대로 잘 작동합니다.

정리해보면, var를 사용하여 변수를 선언하면, 변수 Hosting과 함수 Scope를 고려하면서 코딩을 해야합니다.
따라서 var를 사용하면 전반적으로 코드가 어떻게 작동할지 예상하기 어려운 상황이 발생하게 됩니다.
프로그래머로서는 신경쓸것이 많아져서 피로감을 느끼게 됩니다.
'Front End > Javascript' 카테고리의 다른 글
| [JavaScript] 일급 객체 & 일급 함수 (1) | 2024.10.18 |
|---|---|
| [JavaScript] let 키워드 (0) | 2024.10.18 |
| [JavaScript] 화살표 함수(Arrow Function) (2) | 2024.10.17 |
| [JavaScript] 템플릿 리터럴(Template literals) (0) | 2024.10.17 |
| [JavaScript] Spread Operator(전개 연산자) 함수에서 사용 (2) | 2024.10.17 |