const
const는 "상수"라는 뜻을 가지고 있다.
한번 의미를 할당하면 재할당이 불가능하다.
객체나 배열의 내부 값은 바꿀 수 있다.
const a = 1;
a = 2; --> 불가능
const arr = [1,2];
arr.push(3); --> 배열 내부값 변경 가능 // [1,2,3]
function func(){
const b = 1;
console.log(b);
}
console.log(b); --> 호출하려면 func();을 해야지만 호출된다. 이게 Block Scope
let
자유롭게 바꿀 수 있다.
재할당이 가능하다.
let a = 1;
a = 2; --> 값 변경 가능
여기까지가 기본적인 const와 let의 차이이다.
다음으로는 const와 let의 명확한 차이를 다루겠다.
호이스팅(Hoisting)과 클로저(Closure)/스코프(Scope) 개념
앞으로 더 깊게 다뤄볼 내용은 호이스팅(Hoisting)과 스코프(Scope)개념이다.
클로저(Closure)
함수가 자신이 선언될 때의 외부 변수(스코프)를 기억하고 있는 것
렉시컬 스코핑(Lexical scoping) : 스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
innerFunc();
}
outerFunc(); // 결과 값 : 10
- 함수 outerFunc는 전역에 선언된 함수이므로, 상위 스코프로는 전역 스코프가 있다.
- 함수 innerFunc는 함수 outerFunc 내부에 선언되었으므로, innerFunc의 상위 스코프는 함수 outerFunc가 될 것이다.
function outer() {
let count = 0; // outer 함수 안 변수
return function inner() { // inner 함수가 outer 변수 기억
count += 1;
return count;
};
}
const counter = outer(); // outer 호출, inner 함수 반환
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
내용을 보면, inner의 상위 스코프는 outer이다.
outer를 호출했지만 값으로는 inner 함수가 반환되는 것을 볼 수 있다. 이것이 클로저라는 것이다.
그렇다면 어떻게 클로저는 종료된 데이터를 기억할 수 있는것을까?
해당 함수를 실행 시키면 메모리(heap, call stack)에 데이터를 저장한다. 일반적인 경우 함수가 종료되면 메모리에서 해당 데이터가 사라지게 된다.
call stack은 LIFO구조로 말 그대로 스택구조이다.
(Last In First Out) - 만약 이개념을 모르겠다면 프링글스 통을 기억해라 먼저 들어간게 맨 마지막에 나온다
이러한 call stack은 outer()를 스택으로 쌓는다 (물론 이때, outer보다 아래에 선언된다른 전역변수가 있다면 그것을 outer위로 쌓는다)
그렇게 outer()가 실행뒤에 종료하게 되면 자연스럽게 가비지 컬렉션(Garbage Collection)으로 보내게 되는데, 클로저가 있는 경우에 heap에 유지를 시킨다.
| (이때 heap은 뭐에요?) 동적으로 할당되는 메모리 공간이다. (어디에?) 엔진자체가 call stack 영역과 heap영역으로 나누어서 하나의 메모리 공간에서 관리를 한다. 공간을 분류하여 저장하는 이유는 당연히 알고리즘 구조상 stack과 heap은 공존하기 힘들기 때문이다. stack은 빠른 접근이 가능하지만, heap은 동적으로 할당하여 주기 때문에 느리지만 유연하다는 차이점이 있다. |
즉, 클로저가 있는 경우 해당 함수의 데이터를 가비지 컬렉션으로 보내는것이 아닌 heap이라는 공간에 데이터를 유지시키고, 이후에 해당 함수가 호출되었을때, heap영역에서 데이터를 끌어서 호출되는것이다.
스코프(Scope)
전역, 함수 등등... 해당 데이터를 정의한 위치라고 생각하면 편하다.
위의 클로저 설명의 예제를 보았듯이 outer()내부에 count라는 변수가 지정되었고, 이는 inner()에서 끌어다가 참조할 수 있다.
하지만 outer()의 상위 스코프인 전역스코프의 관점에서는 count라는 변수를 끌어다가 참조할 수 없다.
이것이 스코프이다.
호이스팅(Hoisting)
변수 선언과 함수 선언이 실제 코드 실행 전에 자바스크립트 엔진에 의해 끌어올려지는 현상이다.
// 변수 호이스팅
console.log(a); // 출력값 : undefined
var a = 10;
// 함수 호이스팅
sayHello(); // 출력값 : Hello!
function sayHello() {
console.log("Hello!");
}
1. 변수 호이스팅의 출력값은 undefined(정의되지 않음)
2. 함수 호이스팅의 출력값은 Hello!
이렇게 아래의 데이터를 끌어다가 쓰는것이 호이스팅의 개념이다.
| (변수 호이스팅은 값이 10이 아닌데요?) 맞다. 여기선 var a =10;을 모두 끌어오는게 아닌, var a;를 가져오는것과 같다. 이유는 console.log(a);에서 시스템은 a라는것이 선언된것을 필요로하지 그 값을 필요로 하진 않는다. 값을 필요로하는것 우리들이고 시스템은 그렇게 생각하지 않는다는 것이다. |
하지만, let/const는 이때 다른 값이 출력된다.
console.log(b); // 결과 값 : ReferenceError
let b = 20;
왜 지정되지 않은값이 아니라 에러가 나는걸까?
이는 TDZ(Temporal Dead Zone) 때문이다.
| TDZ란? let이나 const로 선언된 변수가 선언되기 전까지 접근이 금지된 구간이다. |
그렇다면, 왜 let과 const만 TDZ를 둔걸까?
기존 JavaScript는 함수 단위 스코프만 지원했는데 이를 var라고 한다. 즉, var밖에 없던 시절이다. (1995년~2009년)
하지만 호이스팅 때문에 선언 전에 접근을 시도하자, 변수의 유효 범위를 추적하기가 어려워졌다.
때문에, 안정성과 직관적인 변수를 도입했는데 그것이 const와 let이다.
const : 선언 시 초기화가 필수적이다.
let : 재할당이 가능하다.
| [블록 스코프] Block 마다 데이터 참조 범위가 달라진다. |
[함수 스코프] var |
이를 통해 많은 버그들이 사라졌다.
let / const을 언제쓰는 걸까?
const는 고정된 값 / let은 변하는 값인데 언제 어디서 사용 할 수 있는걸까?
| const | let |
| 이름 생년월일 달력(년,월,일) 회사명(고유명사) 서버 주소 |
블로그 방문자 수 현재 페이지 접속자 온도, 날씨 정보 현재 선택된 메뉴 장바구니 상품 갯수 |
'딥 다이브(Front-End 8회차)' 카테고리의 다른 글
| CSS/HTML - 2 (1) | 2025.11.24 |
|---|---|
| 2025-11-21 / HTML의 개요 (0) | 2025.11.21 |