JavaScript는 동적 타입 언어입니다. 변수에는 타입이 없고 값에만 타입이 있어서, 같은 변수에 숫자를 넣었다가 문자열을 넣을 수도 있습니다. 이 장에서는 기본 타입과 객체, 배열과 배열 메서드, Map과 Set, 클래스와 프로토타입, 그리고 데이터를 주고받을 때 쓰는 JSON을 살펴봅니다.
원시 타입
객체가 아닌 값을 원시 값(primitive)이라고 하며, 모두 7가지입니다. 원시 값은 바꿀 수 없고(immutable), 비교할 때는 값 자체를 비교합니다.
console.log(0.1 + 0.2); // 0.30000000000000004 — 부동소수점 오차
console.log(Number("42")); // 42
console.log(Number("42px")); // NaN
console.log(Number.isNaN(NaN)); // true
console.log(2 ** 53 + 1); // 9007199254740992 — 정밀도 한계
console.log(2n ** 53n + 1n); // 9007199254740993n — bigint는 정확합니다
number는 정수와 실수를 구분하지 않는 64비트 부동소수점이라 금액 계산처럼 정확해야 하는 곳에서는 정수 단위(원, 센트)로 바꿔 계산하는 것이 좋습니다. 조건문에서 거짓으로 취급되는 값(falsy)은 false, 0, -0, 0n, "", null, undefined, NaN이며, 그 밖의 값은 빈 배열과 빈 객체를 포함해 모두 참으로 취급됩니다.
객체
객체는 키와 값의 묶음입니다. 점(.)이나 대괄호([])로 속성에 접근하고, 변수 이름과 키가 같으면 줄여 쓸 수 있습니다. 객체는 참조로 전달되므로 다른 변수에 대입해도 같은 객체를 가리킵니다.
const name = "도윤";
const user = {
name, // name: name 의 줄임
age: 29,
"favorite-color": "blue",
introduce() {
return `저는 ${this.name}입니다.`;
},
};
console.log(user.age); // 29
console.log(user["favorite-color"]); // "blue"
console.log(user.introduce()); // "저는 도윤입니다."
for (const [key, value] of Object.entries(user)) {
console.log(key, typeof value);
}
const same = user;
same.age = 30;
console.log(user.age); // 30 — 같은 객체를 가리킵니다
배열과 배열 메서드
배열은 순서가 있는 값의 목록입니다. 반복문 대신 배열 메서드를 쓰면 "무엇을 하려는지"가 코드에 잘 드러납니다. map, filter, reduce는 새 배열이나 값을 돌려주고 원본을 바꾸지 않지만, push, sort, reverse, splice는 원본을 직접 바꿉니다.
const products = [
{ name: "마우스", price: 25000, stock: 3 },
{ name: "모니터", price: 320000, stock: 0 },
{ name: "키보드", price: 89000, stock: 7 },
];
const names = products.map((p) => p.name); // ["마우스", "모니터", "키보드"]
const inStock = products.filter((p) => p.stock > 0); // 재고 있는 상품만
const total = products.reduce((sum, p) => sum + p.price, 0); // 434000
const monitor = products.find((p) => p.name === "모니터");
const anySoldOut = products.some((p) => p.stock === 0); // true
const allCheap = products.every((p) => p.price < 100000); // false
// sort는 원본을 바꾸므로 복사한 뒤 정렬합니다
const byPrice = [...products].sort((a, b) => a.price - b.price);
console.log(byPrice.map((p) => p.name));
sort()에 비교 함수를 주지 않으면 요소를 문자열로 바꿔 정렬하므로 [10, 9, 1].sort()는 [1, 10, 9]가 됩니다. 숫자는 항상 (a, b) => a - b처럼 비교 함수를 넘기세요.
Map과 Set
Map은 어떤 값이든 키로 쓸 수 있는 키-값 저장소이고, Set은 중복 없는 값의 모음입니다. 키가 자주 추가되고 지워지거나 키가 문자열이 아닐 때는 일반 객체보다 Map이 알맞습니다.
const visits = new Map();
visits.set("/home", 10);
visits.set("/about", 3);
visits.set("/home", visits.get("/home") + 1);
console.log(visits.get("/home")); // 11
console.log(visits.has("/blog")); // false
console.log(visits.size); // 2
const tags = new Set(["js", "web", "js", "node"]);
console.log(tags.size); // 3 — 중복이 사라집니다
console.log(tags.has("web")); // true
const unique = [...new Set([3, 1, 3, 2, 1])]; // [3, 1, 2]
클래스와 프로토타입
JavaScript의 객체는 프로토타입이라는 다른 객체에서 속성을 물려받습니다. class 문법은 이 프로토타입 상속을 읽기 쉽게 감싼 것입니다. #으로 시작하는 필드는 클래스 밖에서 접근할 수 없는 비공개 필드입니다.
class Account {
#balance = 0; // 비공개 필드
constructor(owner) {
this.owner = owner;
}
deposit(amount) {
if (amount <= 0) throw new RangeError("금액은 0보다 커야 합니다");
this.#balance += amount;
return this;
}
get balance() {
return this.#balance;
}
static create(owner) {
return new Account(owner);
}
}
class SavingsAccount extends Account {
constructor(owner, rate) {
super(owner);
this.rate = rate;
}
}
const acc = new SavingsAccount("하린", 0.03).deposit(1000);
console.log(acc.balance); // 1000
console.log(acc instanceof Account); // true
console.log(Object.getPrototypeOf(acc) === SavingsAccount.prototype); // true
메서드는 각 객체에 복사되지 않고 프로토타입에 한 번만 저장됩니다. 객체에서 속성을 찾지 못하면 프로토타입, 그 프로토타입의 프로토타입 순으로 올라가며 찾는데, 이를 프로토타입 체인이라고 합니다.
JSON
JSON은 데이터를 주고받을 때 가장 널리 쓰는 텍스트 형식입니다. JSON.stringify는 값을 문자열로, JSON.parse는 문자열을 값으로 바꿉니다.
const data = { id: 1, tags: ["a", "b"], createdAt: new Date(0), secret: undefined };
const text = JSON.stringify(data, null, 2); // 들여쓰기 2칸
console.log(text); // secret은 빠지고, Date는 문자열이 됩니다
const parsed = JSON.parse(text);
console.log(typeof parsed.createdAt); // "string"
// 깊은 복사가 필요하면 structuredClone을 씁니다(Date도 유지)
const copy = structuredClone(data);
JSON에는 함수, undefined, Symbol을 담을 수 없고 Date는 문자열로 바뀝니다. 잘못된 문자열을 JSON.parse에 넘기면 SyntaxError가 발생하므로, 외부에서 받은 데이터는 try...catch로 감싸는 것이 안전합니다.
정리
- 원시 타입 7가지와 객체로 나뉘며, 원시 값은 값으로, 객체는 참조로 다뤄집니다.
- 배열 메서드
map, filter, reduce, find를 익히면 반복 코드가 훨씬 간결해집니다. sort는 원본을 바꾼다는 점에 주의합니다. - 키가 다양하거나 자주 바뀌면
Map, 중복 제거에는 Set을 씁니다. class는 프로토타입 상속을 감싼 문법이며 # 비공개 필드를 지원합니다.- JSON 변환 시 Date와
undefined가 어떻게 바뀌는지 알아 두세요.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.