선언, 할당, 범위로 나눠서 알아두면 편함
재선언 | 재할당 | 범위 | |
---|---|---|---|
var | o | o | function |
let | x | o | { } |
const | x | x | { } |
var 이름 = 'Kim';
var 이름 = 'Park';
var는 이렇게 같은 이름의 변수를 두번 이상 재선언할 수 있다.
var 이름; // 선언
이름 = 'Park'; // 할당
let 이름 = 'Kim';
이름 = 'Park'; // ✅
const 나이 = 30;
나이 = 40; // ❌
const는 애초에 constant의 약자로 바뀌지 않는 일정한 값을 뜻함
➡️ const로 만들면 값 재할당이 불가능
그러나...⭐ const로 오브젝트를 만들었을 때 오브젝트 내부 값을 변경해도 에러가 안남!
const 변수 자체를 재할당한게 아니기 때문이다.const 사람 = { 이름 : 'Kim' } 사람.이름 = 'Park'; ✅
const 사람 = { 이름 : 'Kim' }
Object.freeze(사람);
사람.이름 = 'Park'; ❌
사람 오브젝트가 수정 불가능해짐
✨변수의 선언 & 함수의 선언을 변수 범위 맨위로 끌고오는 현상
→ js는 원래 이렇게 동작한다.
~~~~ // 코드
var 나이 = 30;
function 함수(){}
var 나이;
function 함수(){}
~~~~~~ // 코드
나이 = 30;
근데 여기서 중요한 건..
⭐ var는 호이스팅 시 undefined가 자동으로 할당이 되고
let과 const는 undefined가 할당이 되지 않는다!
console.log(이름); // undefined
var 이름 = 'Kim';
console.log(이름); // ⚠️ 에러!
let 이름 = 'Kim';
undefined는 변수가 선언은 되었는데 값이 아무것도 할당되지 않았을 때 출력되는 값이다. 즉, 정해지지 않은 값 이라고 생각하면 된다.
함수의 선언 부분을 맨 위로 끌고 오기 때문에 함수 선언 전에 함수 호출이 가능하다.
함수();
function 함수() {
let 안녕 = 'Hello!';
}
근데 여기서 주의할 점은..
function 함수(){} : 전부가 Hoisting 됨
var 함수 = function(){} : 선언 부분만 Hoisting 됨
함수();
var 함수 = function() {
console.log(안녕);
var 안녕 = 'Hello!';
}
➡️ 딱 var 함수
만 위로 당겨지기 때문에 에러가 난다.
// before
var 나이 = 20;
var 이름 = 'Kim';
var 성별;
// after
var 나이 = 20, 이름 = 'Kim', 성별;
모든 곳에서 쓸 수 있는 변수
바깥에 있는 변수는 안에서 그대로 사용할 수 있음.
var 나이 = 20; // 바깥
function 함수(){ // 안
console.log(나이); // ✅
}
window.이름 = '김';
Q. 코드가 다음과 같을 때 출력되는 값은?
let a = 1;
var b = 2;
window.a = 3;
window.b = 4;
console.log(a + b);
A. a는 1, b는 4가 출력된다.
b가 4가 되는 이유는 var b = 2
와 window.b = 4
는 거의 동일한 기능을 하는 코드기 때문에 b는 그냥 4로 재할당 된 것이다.
a의 경우 let으로 선언한 값이 글로벌 변수보다 범위가 작기 때문에 '나랑 더 가까운 값'인 1을 참조한 것이다.
Q. 콘솔창에 1초에 한번씩 1부터 5까지의 정수를 출력해주고 싶을 때 다음 코드를 어떻게 수정해야 할까?
for (var i = 1; i < 6; i++) {
setTimeout(function() { console.log(i); }, i*1000 );
}
이 코드는 1초마다 6이 출력돼서 원하는 대로 동작하지 않는다.
A. let 변수를 사용한다.
setTimeout안에 들어있는 console.log(i)
는 바로바로 실행되는 코드가 아니다. 따라서 반복문이 다 완료된 후 차례대로 실행이 되는데 이 시점에서 i 값은 반복문이 다 실행되고 전역변수 var i = 6;
로 남아있다. 따라서 계속 6이 출력되는 것이다.
for (let i = 1; i < 6; i++) {
setTimeout(function() { console.log(i); }, i*1000 );
}
✨ let 변수는 범위가 중괄호이므로 i값이 아래와 같이 for 반복문 내에 남아있게 된다.
for (let i = 1; i < 6; i++) {
let i = 3
setTimeout(function() { console.log(i); }, i*1000 );
}
따라서 반복문이 다 끝나고 console.log(i)
가 실행될 때 for 반복문 내에 남아있는 i값을 가져다 쓰게 된다.
Q. 버튼을 누르면 모달창을 띄우고 싶을 때 다음 코드를 어떻게 수정해야 할까?
<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');
for (var i = 0; i < 3; i++){
버튼들[i].addEventListener('click', function(){
모달창들[i].style.display = 'block';
});
}
</script>
A. let 변수를 사용한다.
위의 문제와 유사하다.
✨ 이벤트리스너 내부 코드 모달창들[i].style.display = 'block';
는 반복문이 다 끝나고 나중에 클릭이라는 현상이 일어날 때 실행이 된다.
따라서 클릭이 일어났을 때 i값을 채우려고 봤더니 전역변수 var i = 3;
밖에 남아있지 않으므로 i에 3을 채우게 되는 것이다.
for (let i = 0; i < 3; i++){
let i = 2
버튼들[i].addEventListener('click', function(){
모달창들[i].style.display = 'block';
});
}
이렇게 let을 사용하면 반복문이 다 끝나고 나서도 중괄호 안에 let = ~
가 남아있기 때문에 그 값을 모달창들[i].style.display = 'block';
의 i값으로 가져다 쓰게 된다.