본문 바로가기

Front-End: Web/JavaScript

[코딩애플] js part 1-5. 변수 신문법 총정리

반응형

1. var let const와 선언, 할당 범위

변수란?

자료 임시 저장공간

변수 만드는 방법

var

var 이름 = 'Kim';

let, const

  • ES6 신문법. var 키워드를 대체한다.
let 이름 = 'Kim';

var vs let,const

varletconst

**재선언** 가능 불가능 불가능
**재할당** 가능 가능 불가능
**범위** function 중괄호 {} 중괄호 {}

재선언

var 이름 = 'Kim';
var 이름 = 'Park';
var 이름 = 'Heo'; // ➡️ 재선언 가능

let 나이 = 20;
let 나이 = 30; // ➡️ Error 발생: 재선언 불가능
const 나이2 = 20;
const 나이2 = 30;  // ➡️ Error 발생: 재선언 불가능

재할당

var 이름 = 'Kim';
이름 = 'Park';

let 이름1 = 'Kim';
이름1 = 'Park';

const 이름2 = 'Kim';
이름2 = 'Kim'; // ➡️ Error 발생: 재할당 불가능

const 변수 안의 값은 변경은 자유롭다(이건 재할당이 아님).

const 사람 = { 이름: 'Kim' };
사람.이름 = 'Park'

범위

  1. var - function
function 함수(){
	var 이름 = 'Kim'; // function 안에서만 존재함
    console.log(이름); // Kim;
}
console.log(이름); // Uncaught ReferenceError: 이름 is not defined.
  1. let, const - {중괄호}
if(true){
    let 이름 = 'Park'; // '이름'은 if 안에서만 존재함
}

for(let v = 1;){ // v는 for 안에서만 존재함
    ...
}

수정 불가능한 오브젝트 만들고 싶다면? Object.freeze()

const 사람 = { 이름: 'Kim'};
Object.freeze(사람);
사람.이름 = 'Park';

console.log(사람); // {이름: 'Kim'}

Object.freeze()로 얼려버리면 오브젝트 내 값을 바꾸려고 해도 수정이 안된다. 만약 'use strict'로 엄격 모드를 켜고 오브젝트 값을 바꾸려고 시도하면 에러가 뜬다.

2. Hoisting, 전역변수, 참조

변수의 Hoisting 현상

Hosting이란?

  • 변수의 선언을 변수 범위 맨위로 끌고오는 현상(자바스크립트 언어 자체가 그럼)

변수를 만들면 Hoisting 현상이라는게 일어난다.

...

var 나이 = 30;

이 코드를 자바스크립트는

var 나이;
...

나이 = 30;

이렇게 해석한다. 이게 바로 Hoisting 현상이다.

아래 예제 코드로 한번 확인해보자.

console.log(나이); // undefined
var 나이 = 30;
console.log(나이); // 30

보통 변수가 선언 안했는데 먼저 출력하면 error: 변수 '나이' is not defined 에러가 떠야 한다. 근데 선언하는 코드가 있으니까 에러가 안뜨고 'undefined' 뜬다.

undefined

  • 변수를 선언만 하고 아직 할당하지 않은 자료형

즉, 위의 코드는 아래 코드와 동일하다.

var 나이;
console.log(나이);
나이 = 30;
console.log(나이);

변수 동시에 여러 개 만들기

var 나이 = 20;
var 이름 = 'ㅁ';
var 성별;
var 나이 = 20, 이름 = 'ㅁ', 성별;

전역 변수

전역 변수

**모든 곳에서 쓸 수 있는 변수**다.

  • 함수 바깥에서 정의한 변수는 함수 안에서 쓸 수 있다.
var 나이 = 20; // 전역 변수 (바깥)

function 함수(){
	console.log(나이); // (안)
}

함수(); // 나이

지역 변수

function 함수() {
    var 이름 = 'Kim'; // 지역 변수 
}

window로 전역변수 만들기

window가 **자바스크립트의 기본 함수를 담은 {오브젝트} + HTML 기본 DOM** 이라고 했으니까.

var 나이 = 20;
window.이름 = '김';

console.log(이름); // 김
console.log(window.이름); // 김

전역변수로 함수를 넣을 수도 있다.

window.이름 = function() { ... }
console.log(window.이름); // f

전역변수 만들기: 키워드 vs window

**window로 전역변수를 만드는게 더 낫다**. 왜냐하면 더 한눈에 구분하기 좋기 때문이다.

❗함수 선언도 Hoisting

자바스크립트에선 함수 선언도 Hoisting한다. 그래서 아래와 같은 코드는

var 나이 = 20;
window.이름 = '김';

function 함수() {
    ...
}

자바스크립트에선 이렇게 함수를 위로 올려서 해석한다.

function 함수() {
    ...
}

var 나이 = 20;
window.이름 = '김';

연습문제

if(true){
    let a = 1;
    var b = 2;
    if(true){
        let b = 3;
    }
    console.log(b); // 2
}

콘솔하면 2가 나온다. 왜냐하면 let은 생존범위가 중괄호이기 때문에 if(true){}내에서만 살고 죽기 때문이다. 따라서

if(true){
    let a = 1;
    var b = 2;
    console.log(b); // 2
}

와 동일하다고 봐도 무방하다.

3. 연습문제

1~4. 다음 코드의 콘솔창 출력결과는 무엇일까요?

(문제1)

함수();
function 함수() {
  console.log(안녕);
  let 안녕 = 'Hello!';
} 

➡️ Error! ReferenceError: Cannot access '안녕' before initialization

var은 Hoisting하면서 undefined가 할당되지만, **let 변수는 Hoisting이 되긴 하지만 특이하게도 undefined라는 값이 할당(initialization)되지 않는다**. 그래서 출력하면 에러를 뿜는다. 선언과 할당 사이에 시간차가 있기 때문에 이런 현상이 일어나는 것이고, let 변수는 그래서 쓸 수 없다.

let 안녕; // let 키워드는 initialization이 안됨
function 함수(){
    console.log(안녕); // 에러. 값이 할당되지 않음
    let 안녕 = 'Hello';
}

그래서 그냥 **let, const 변수는 더 엄격하게 쓸 수 있는 변수구나** 라고 외우자!

 

(문제2)

함수();
var 함수 = function() {
  console.log(안녕);
  var 안녕 = 'Hello!';
} 

➡️ Error! TypeError: 함수 is not a function

'함수가 아닌데요?'라는 에러가 난다.

Line2에 함수 선언부분을 잘 보면 function 키워드 대신 변수를 만드는 것처럼 함수와 선언을 할당하고 있다. 이렇게 함수를 만들어도 Hoisting은 되는데, 근데 **Hoisting은 변수의 선언부분만 된다**고 했다.

그래서 변수 선언부분만 맨 위로 끌어올려지는데,

var 함수;
함수(); // 에러. undefined라서 함수가 아닌 값을 함수로 실행하면 에러 발생함.
함수 = function(){ ... }

함수();를 실행할 때 아직 함수는 변수로 선언만 했고 undefined인 상태이다. 소괄호를 붙여봤자 아직은 함수가 아니기 때문에 실행이 되지 않아서 에러가 발생한다(**함수가 아닌 변수에 소괄호를 붙이면 함수가 아니라고 에러를 뿜어준다**).

(문제3)

let a = 1;
var 함수 = function() {
  a = 2;
}
console.log(a);

➡️ 1

왜냐하면 변수'함수'에 함수를 만들고 그 함수 안에서 a=2라고 값을 변경시켰지만, 함수를 정의만 했지 실행은 하지 않았기 때문에 a=2라는 부분은 없는 것과 마찬가지다.

let a;
a = 1;
console.log(a); // 1

(문제4)

let a = 1;
var b = 2;
window.a = 3;
window.b = 4;

console.log(a + b);

➡️ a=1, b=4. 답은 5.

b는 var b=2와 window.b=4 는 거의 동일한 기능을 하는 코드이기 때문에 **b가 그냥 4로 재할당**되었다고 보면 된다.

a는 let 변수로 1을 할당하고 글로벌 변수로 3을 할당했다. 근데 이 경우에는 a를 사용했을 때 좀 더 범위가 작고 가까운 1을 참조해서 사용한다(**자바스크립트 변수를 사용할 때, 참조할만한 변수가 내 주변에 없으면 계속 상위 중괄호로 시선을 돌리면서 참조**한다).

(문제5)

**콘솔창에 1초에 한번씩 1부터 5까지의 정수를 출력해주고 싶습니다.**

저번 연습문제에서 setTimeout이라는 유용한 함수를 배운 것 같습니다. 그래서 코드를 이렇게 작성했습니다.

setTimeout(function() { console.log(1); }, 1000 ); 
setTimeout(function() { console.log(2); }, 2000 ); 
setTimeout(function() { console.log(3); }, 3000 ); 
setTimeout(function() { console.log(4); }, 4000 ); 
setTimeout(function() { console.log(5); }, 5000 ); 

그럼 1초마다 1~5까지의 숫자를 콘솔창에 출력해줍니다. 하지만 반복되는 코드가 보기 싫어서 반복문 안에 담았습니다.

for (var i = 1; i < 6; i++) { 
  setTimeout(function() { console.log(i); }, i*1000 ); 
}

논리적으로 완벽한 for 반복문입니다. 그런데 반복문으로 축약하자마자 제대로 작동하지 않습니다. 계속 5라는 숫자가 1초마다 출력되네요.

**Q. 위 코드는 왜 의도대로 동작하지 않는 것이죠? 설명해보십시오.**

**그리고 해결할 방법은 무엇일까요?**

➡️자바스크립트 입장에서 코드를 해석해보자.

자바스크립트는 반복문을 만나면 반복문 내의 코드를 반복해서 실행한다. 반복문이 i가 0부터 5가 되기 전까지 반복해주세요!라고 적혀있으니 총 5번 반복된다. 근데 내부 코드는 setTimeout이다. x초 후에 콜백함수 내의 console.log(i)를 실행해주세요~라고 한다. 그래서 console.log(i) 부분은 반복문을 실행하면서 같이 실행되지 않는다. 나중에 실행되기로 예약된다.

그리고 1초가 지나고 setTimeout의 콜백함수 내에 있는 console.log(i)가 발동된다. i에 값을 넣으려고 i를 찾아보니 i=5다. 왜냐하면 반복문을 돌면서 i값이 1,2,3,... 차례대로 바뀌다가 i=5일때 종료되었기 때문이다. 그리고 i는 var로 만든 전역 변수다.

그래서 i값을 쓰려고 보니 전역변수 i=5밖에 없어서 5를 집어넣고, setTimeout이 x초 지날 때마다 수행될 때마다 모두 콘솔창에 5가 출력된 것이다.

해결하기

**해결책은 var를 let으로 바꾸는 것**이다. let은 범위가 중괄호라고 했다! for 반복문도 중괄호에 해당된다.

그리고 이제 1초 후 console.log(i)가 실행될 떄 i 값을 채우려고 살펴보면, **i값이 for 반복문 내에 아직 그대로 남아있기 때문**에 각각 1,2,3,4,5 값을 가져다 쓴다. 그래서 var을 쓸 때처럼 계속 5를 출력해주는게 아니라 1,2,3,4,5를 출력해준다.

(문제6)

**버튼을 누르면 모달창을 띄우고 싶습니다.**

버튼(button)과 모달창(div)가 3개 있습니다.

<div style="display : none">모달창0</div>
<div style="display : none">모달창1</div>
<div style="display : none">모달창2</div>

<button>버튼0</button>
<button>버튼1</button>
<button>버튼2</button>

<script>
  //?
</script>

지금 display : none 덕분에 모달창이 아무것도 안보이는 상태입니다.

자바스크립트를 잘 짜서 0번째 버튼을 누르면 0번째 모달창, 1번째 버튼을 누르면 1번째 모달창을 보여주고 싶습니다. 그럼 코드를 어떻게 짜면 될까요?

여기까진 기본 자바스크립트 내용이니 친절히 알려드리겠습니다.

<div style="display : none">모달창0</div>
<div style="display : none">모달창1</div>
<div style="display : none">모달창2</div>

<button>버튼0</button>
<button>버튼1</button>
<button>버튼2</button>

<script> 
var 버튼들 = document.querySelectorAll('button');
var 모달창들 = document.querySelectorAll('div');

버튼들[0].addEventListener('click', function(){
  모달창들[0].style.display = 'block';
});

버튼들[1].addEventListener('click', function(){
  모달창들[1].style.display = 'block';
});

버튼들[2].addEventListener('click', function(){
  모달창들[2].style.display = 'block';
});

</script>

document.querySelectorAll은 jQuery의 $('') 셀렉터와 매우 유사합니다. 동시에 여러 요소를 찾아 어레이 비슷한 자료형에 담아줍니다.

아무튼 이렇게 쭉 쓰면

0번째 버튼을 누르면 0번째 모달창,

1번째 버튼을 누르면 1번째 모달창을 보여줍니다.

그런데 비슷한 코드들이 좀 보이죠? 이걸 반복문 안에 담아서 한번 다시 개발해보겠습니다.

두근두근

<script> 
var 버튼들 = document.querySelectorAll('button');
var 모달창들 = document.querySelectorAll('div');

for (var i = 0; i < 3; i++){
  버튼들[i].addEventListener('click', function(){
    모달창들[i].style.display = 'block';
  });

}

</script>

이렇게 반복문으로 반복적인 코드를 축약가능합니다.

그런데 문법에 맞게 쓰긴 했는데 모달창이 제대로 작동하지않고 있습니다.

**Q. 위 코드는 왜 의도대로 동작하지 않는 것이죠? 설명해보십시오.**

**그리고 해결할 방법은 무엇일까요?**

➡️ 이 문제도 5번 문제와 거의 동일하다!

문제에서 반복문이 i가 0부터 3이 되기 전까지 반복해주세요!라고 썼으니 총 3번 반복된다.

근데 내부 코드는 **addEventListener**이다. 클릭되면 콜백함수 내의 코드가 실행된다. 그래서 이 부분은 반복문이 실행될 때 동시에 실행되지 않는다. 나중에 사용자가 클릭할 때 실행된다.

위의 문제랑 똑같이, 누군가가 버튼을 클릭하면 콜백함수 내의 코드(모달창들[i].style.display = 'block';)가 발동된다. i를 쓰고 싶어서 주변을 살폈더니 i는 이미 반복문이 실행된 후라 3이 되어있다. 또한 i는 var로 만든 전역변수다. 그래서 i값을 쓰려고 보니 3밖에 없어서 모두 3을 집어넣는다.

해결하기

이 또한 해결책은 i 변수를 var 대신 let으로 바꾸는 거다. for 반복문도 중괄호에 해당되기 때문이다.

반복문이 돌고 나서도 let i는 {for 반복문}에 남아있기 때문에 그걸 콜백함수 내의 코드가 각각 0,1,2,3 값을 가져다 쓰게 된다. 그러면 이제 의도한 i값으로 코드가 잘 실행된다.

반응형