
var 재선언 O 재할당 O
let 재선언 X 재할당 O
const 재선언 X 재할당 X
var, let은 둘 다 hoisting(끌어올리기)이 되지만 동작이 다르다.
var는 변수를 만났을 때 그 이름으로 메모리 공간을 만들고 일단 undefined라고 적어 놓는다.
let(const도 마찬가지)은 변수를 만났을 때 그 이름을 기억해두되, 실제 코드 줄에 도착하기 전까지는 절대 접근 금지시킨다.(초기화 안 함. 일시적 사각지대 TDZ에 빠짐.)
즉, 둘 다 엔진이 미리 알고 끌어올리는 건 맞지만 var만 관대하게 undefined로 초기화까지 해주는 것이다.
Javascript에서 TDZ란? Temporal Dead Zone, 일시적 사각지대.
선언은 되어 있지만 초기화(값 할당)가 되기 전까지는 접근하면 에러가 나는 구간
즉, 변수가 (hoisting으로 인해) 이미 존재는 하지만 초기화되지 않아 사용할 순 없는 구간.
var는 호이스팅될 때 선언과 동시에 undefined로 초기화되므로 메모리에 공간이 생기자마자 값이 채워지기 때문에 TDZ가 존재할 틈이 없다. TDZ는 let과 const에서만 존재한다. let, const에서는 hoisting으로 선언만 먼저 되고 실제 코드에서 그 변수 줄에 도달하기 전까지 초기화되지 않는다. 그 사이 공백이 TDZ이다.
console.log('oldVar :', oldVar); // undefined
var oldVar = 42;
var의 경우 위처럼 선언문 이전에 값을 찍어봐도 에러가 뜨지 않고 undefined라고 나온다.
hello() // OK
function hello() { // hoisting 되어 스코프 시작부터 함수 값이 준비된다.
console.log("hi");
}
선언, 초기화, 할당이 한 번에 호이스팅된다. TDZ가 없다.
var - TDZ는 없지만, undefined로 TypeErrorhi(); // TypeError: hi is not a function
var hi = function () {};
: var hi는 호이스팅되지만 값은 undefined로 초기화됨.
따라서 hi()는 TDZ에러가 아니라 undefined를 함수처럼 호출해서 TypeError가 발생.
let/const — TDZ 발생, ReferenceErrorhey(); // ReferenceError: Cannot access 'hey' before initialization
const hey = function () {};
yo(); // ReferenceError
let yo = () => {};
변수 선언 방식(let, const)을 따르기 때문에 TDZ의 영향을 받는다.
참고: const는 선언과 동시에 반드시 값을 할당해야 하므로, TDZ를 통과하자마자 바로 값까지 완벽하게 갖게 된다.
let/const는 스코프 시작부터 바인딩은 만들어지지만, 초기화 전 접근이 금지(TDZ).
그래서 ReferenceError가 발생.
class 선언도 TDZ
new A(); // ReferenceError
class A {}
let a = 1;
function test() {
console.log(a);
let a = 2;
}
test();
→ TDZ로 인해 ReferenceError가 뜬다.
let a = 10;
{
console.log(a);
var a = 20;
}
→ 파싱 단계에서 막히고 "이미 선언됐다"며 syntaxError가 뜬다.
var은 블록 스코프를 무시하고 함수/전역에 붙기 때문이다.
그러므로 블록안에 도달했을 때 같은 스코프에 let a가 이미 있는데 var a로 또 선언하게 되는 형국.
cf. 면접 질문으로 나올 수 있는 내용이다.
전역 공간(가장 바깥쪽)에서 선언했을 때 var와 let에는 다음과 같은 차이가 있다.
브라우저 환경에는 window라는 거대한 전역 객체가 있다.
// var로 선언하면 'window'의 속성이 된다.
var a = 10;
console.log(window.a); // 10 (접근 가능!)
// let/const로 선언하면 'window'에 붙지 읺는다.
let b = 20;
console.log(window.b); // undefined (접근 불가)

// String
let name = "Sue";
let country = "Korea";
let introduce = name + country //SueKorea
let introduceText = `${name}는 ${country}에 거주한다.` // "템플릿 리터럴 문법"
// number (숫자)
let age = 27;
console.log('number:', age, typeof age);
// number: 27 number
// undefined (아직 값 없음)
let data; console.log('undefined:', data, typeof data);
// undefined: undefined undefined
// null (명시적 없음)
let emptyValue = null;
console.log('null:', emptyValue, typeof emptyValue);
// null: null object
// typeof null === "object" (⚠️ null의 타입은 object이다. - 자바스크립트 설계 결함)
// 객체 (object)
let person = { nickname: "fox", score: 99 };
console.log('object:', person, typeof person);
// object: {nickname: 'fox', score: 99} object
// 배열 (array, 실제로는 object)
let nums = [10, 20, 30];
console.log('array:', nums, typeof nums);
// array: [10, 20, 30] object
// 객체 선언
const animal = {
species: "Jindo",
age: 8,
isCute: true
};
console.log('object:', animal); // object: {species: "Jindo", age: 8, isCute: true}
console.log('typeof animal:', typeof animal); // typeof animal: object
// 함수 (function)
function sayHello() { return "hello!"; }
console.log('function:', sayHello, typeof sayHello);
// function: ƒ sayHello() { return "hello!"; } function
// 객체 선언
const animal = {
species: "cat",
age: 5,
isCute: true
};
console.log('object:', animal); // {species: "cat", age: 5, isCute: true}
console.log('typeof animal:', typeof animal); // object
// 객체를 JSON 문자열로 변환
const jsonAnimal = JSON.stringify(animal);
// 변환된 JSON 문자열 출력
console.log('json string:', jsonAnimal); // {"species":"cat","age":5,"isCute":true}
// 다시 객체로 변환
const parsedAnimal = JSON.parse(jsonAnimal);
console.log('parsed object:', parsedAnimal);//{species: "cat", age: 5, isCute: true}
console.log('typeof parsedAnimal:', typeof parsedAnimal); // object
NaN(Not a Number)는 잘못된 숫자 연산 결과로 만들어지며, 예를 들어 "abc" * 5의 결과가 NaN이다.
NaN은 자기 자신과도 ===으로 비교할 수 없는 값이다.
// NaN 판별하는 법 : isNaN()
console.log('isNaN("abc" * 5):', isNaN(weirdValue)); // true
// NaN은 숫자가 아니란 뜻이지만, 자바스크립트에서는 숫자 타입(number)의 특수한 값이다.
// NaN 판별은 isNaN(값) 함수를 사용한다.
참고) ⚠️ typeof 연산자가 *(곱하기) 연산자보다 우선순위가 더 높다.

참고) 위 이미지에서처럼 나타나는 콘솔창의 undefined는 내가 시킨 일(console.log())의 반환값이 없을 때 나타난다.
// NaN (Not a Number)
let weirdValue = "abc" * 5;
console.log('NaN:', weirdValue, typeof weirdValue);
// NaN, NaN number
let num = 10;
let str = "20";
const result = num + str;
console.log(result); //1020
숫자를 문자열로 묵시적 형 변환이 이뤄졌다.
: 존재하는 값을 추려내는 기능
null과 undefined가 아닌 모든 값을 반환
let var1;
let var2 = 10;
let var3 = 20;
let var4 = var1 ?? var2;
console.log(var4); // 10
let var5 = var1 ?? var2 ?? var3;
console.log(var5); // 10
let var6 = var2 ?? var3;
console.log(var6);
// 10 (둘 다 null이나 undefined가 아니므로 첫 번째 값인 var2가 반환됨)
: 존재하는 값을 추려내는 기능
false, null, undefined가 아닌 모든 값을 반환
let var7 = var1 || var2;
console.log(var7); // 10 (var1이 false이므로 var2가 반환됨)
let var8 = var1 || var2 || var3;
console.log(var8); // 10 (var1이 false이므로 var2가 반환됨)
// JS에서 || 는 단순히 true/false만 반환하는 것이 아니라
// “첫 번째로 truthy한 값을 그 값 그대로 반환”한다.
// 그리고 없으면 마지막 값을 반환한다.
let var9 = var2 || var3;
console.log(var9); // 10 (둘 다 false가 아니므로 첫 번째 값인 var2가 반환됨)
|| && ?? 정리왼쪽부터 평가하여 첫 번째 truthy 값을 “그 값 그대로” 반환, 없으면 마지막 값 반환
0 || 10 || 20 // 10
"" || "A" // "A"
false || 0 || "" // "" (모두 falsy면 마지막 "")
왼쪽부터 평가하여 첫 번째 falsy 값을 “그 값 그대로” 반환, 모두 truthy면 마지막 값 반환
10 && 20 // 20 (둘 다 truthy → 마지막)
0 && 20 // 0 (첫 falsy를 반환)
"hi" && "" // "" (첫 falsy)
?? (Nullish coalescing)
null 또는 undefined 일 때만 오른쪽을 사용
??0, "", false는 “값이 있는 것”으로 취급해서 그대로 유지
0 ?? 10 // 0
"" ?? "A" // ""
false ?? true // false
null ?? 10 // 10
undefined ?? 10 // 10
“기본값”을 주고 싶은데 0이나 ""도 유효 값이면 → ??가 안전하다.
let page = inputPage ?? 1; // inputPage가 0이면 0 유지
// (|| 쓰면 0이 falsy라 1로 바뀌어버릴 수 있음)
let fruit = "apple";
switch (fruit) {
case "apple":
console.log("빨간색입니다.");
break;
case "banana":
case "lemon":
console.log("노란색입니다.");
break;
case "orange":
console.log("주황색입니다.");
break;
default:
console.log("알 수 없는 과일입니다.");
}
⚠️ 만약 break가 없다면 다음 case도 계속 실행된다.(fall-through)
이처럼 switch는 break 실수로 버그 발생할 우려가 있고,
확장성이 떨어지기 때문에 JS 개발자들은 switch를 잘 쓰지 않는다.