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'
범위
- var - function
function 함수(){
var 이름 = 'Kim'; // function 안에서만 존재함
console.log(이름); // Kim;
}
console.log(이름); // Uncaught ReferenceError: 이름 is not defined.
- 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값으로 코드가 잘 실행된다.
'Front-End: Web > JavaScript' 카테고리의 다른 글
[코딩애플] js part 1-7. Spread Operator (...)와 apply / call (0) | 2022.10.09 |
---|---|
[코딩애플] js part 1-6. 자바스크립트가 문자 다루는 신기한 방법 - Template literals (strings) (0) | 2022.10.09 |
[코딩애플] js part 1-4. this & arrow function 연습문제 3개 (0) | 2022.10.09 |
[코딩애플] js part 1-3. Arrow function은 function을 대체하는 신문법이 아님 (0) | 2022.10.09 |
[js] event.currentTarget vs event.target (0) | 2022.10.09 |