출시·고도화 중
JavaScript 안내서 · 3/6
JavaScript는 동적 타입 언어입니다. 변수에는 타입이 없고 값에만 타입이 있어서, 같은 변수에 숫자를 넣었다가 문자열을 넣을 수도 있습니다. 이 장에서는 기본 타입과 객체, 배열과 배열 메서드, Map과 Set, 클래스와 프로토타입, 그리고 데이터를 주고받을 때 쓰는 JSON을 살펴봅니다.
객체가 아닌 값을 원시 값(primitive)이라고 하며, 모두 7가지입니다. 원시 값은 바꿀 수 없고(immutable), 비교할 때는 값 자체를 비교합니다.
| 타입 | 예 | typeof 결과 |
|---|---|---|
| string | "안녕" | "string" |
| number | 42, 3.14, NaN | "number" |
| bigint | 9007199254740993n | "bigint" |
| boolean | true, false | "boolean" |
| undefined | undefined | "undefined" |
| null | null | "object"(오래된 설계 실수) |
| symbol | Symbol("id") | "symbol" |
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이 알맞습니다.
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.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로 감싸는 것이 안전합니다.
map, filter, reduce, find를 익히면 반복 코드가 훨씬 간결해집니다. sort는 원본을 바꾼다는 점에 주의합니다.Map, 중복 제거에는 Set을 씁니다.class는 프로토타입 상속을 감싼 문법이며 # 비공개 필드를 지원합니다.undefined가 어떻게 바뀌는지 알아 두세요.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.