JavaScript는 1995년 Brendan Eich가 단 10일 만에 설계한 언어다. 처음엔 웹 브라우저에서 간단한 동작을 처리하는 스크립트 언어로 시작했지만, 지금은 프론트엔드·백엔드(Node.js)·모바일(React Native)까지 아우르는 범용 언어가 됐다.
이 글은 JavaScript를 다시 처음부터 훑으면서 기본기를 단단하게 다지는 데 목적이 있다.
1. 변수 선언 — var / let / const
JavaScript에는 변수를 선언하는 방법이 세 가지 있다. 셋의 차이를 모르면 예상치 못한 버그가 생긴다.
// var: 함수 스코프, 호이스팅 O, 재선언 O → 사용 지양
var x = 1;
var x = 2; // 재선언 허용 (버그 유발)
console.log(x); // 2
// let: 블록 스코프, 재선언 X, 재할당 O
let count = 0;
count = 10; // 재할당 가능
// let count = 20; // SyntaxError: 재선언 불가
// const: 블록 스코프, 재선언 X, 재할당 X
const MAX = 100;
// MAX = 200; // TypeError
// 단, const 객체/배열의 내부는 변경 가능
const user = { name: '홍길동' };
user.name = '김철수'; // 허용 (참조 자체는 변경 안 함)
user.age = 30; // 속성 추가도 허용
JavaScript
복사
구분 | 스코프 | 재선언 | 재할당 | 호이스팅 |
var | 함수 | |||
let | 블록 | |||
const | 블록 |
TDZ (Temporal Dead Zone): let/const는 선언 전에 접근하면 ReferenceError가 발생한다. 호이스팅은 되지만 초기화 전 구간에서 접근 자체가 막혀 있다.
2. 데이터 타입
2-1. 원시 타입 (Primitive)
// Number: 정수와 부동소수점을 하나의 타입으로
const n = 42;
const pi = 3.14;
const inf = Infinity;
const nan = NaN; // Not a Number (계산 실패 시)
console.log(0.1 + 0.2); // 0.30000000000000004 (부동소수점 주의)
// BigInt: 2^53 - 1보다 큰 정수
const big = 9007199254740991n;
const big2 = BigInt('12345678901234567890');
// String
const str1 = '작은따옴표';
const str2 = "큰따옴표";
const str3 = `템플릿 리터럴: ${n + 1}`; // 표현식 삽입 가능
// Boolean
const isTrue = true;
const isFalse = false;
// null: 명시적으로 '값 없음'
const empty = null;
// undefined: 값이 할당되지 않은 상태
let notAssigned; // undefined
// Symbol: 유일한 식별자
const id1 = Symbol('id');
const id2 = Symbol('id');
console.log(id1 === id2); // false (항상 고유)
JavaScript
복사
2-2. 타입 확인
console.log(typeof 42); // 'number'
console.log(typeof 'hello'); // 'string'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object' ← JS의 유명한 버그
console.log(typeof {}); // 'object'
console.log(typeof []); // 'object'
console.log(typeof function(){}); // 'function'
// 배열 판별에는 Array.isArray 사용
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
// null 판별
console.log(empty === null); // true
JavaScript
복사
2-3. 암묵적 형 변환
// 느슨한 비교 (==): 형 변환 후 비교
console.log(0 == false); // true
console.log('' == false); // true
console.log(null == undefined); // true
console.log(1 == '1'); // true
// 엄격한 비교 (===): 형 변환 없이 타입까지 비교 → 항상 이걸 써야 함
console.log(0 === false); // false
console.log(1 === '1'); // false
// falsy 값: false로 취급되는 값
// false, 0, -0, 0n, '', null, undefined, NaN
console.log(Boolean(0)); // false
console.log(Boolean('')); // false
console.log(Boolean(null)); // false
console.log(Boolean([])); // true ← 빈 배열은 truthy
console.log(Boolean({})); // true ← 빈 객체도 truthy
JavaScript
복사
3. 연산자
// 산술
console.log(10 % 3); // 1 (나머지)
console.log(2 ** 10); // 1024 (거듭제곱, ES2016)
// 논리
console.log(true && false); // false
console.log(true || false); // true
console.log(!true); // false
// 단락 평가 (Short-circuit evaluation)
const name = null;
console.log(name || '기본값'); // '기본값' (||: 왼쪽 false면 오른쪽)
console.log(name && name.length); // null (&&: 왼쪽 false면 왼쪽 반환)
// Nullish Coalescing (??, ES2020): null/undefined일 때만 오른쪽
console.log(null ?? '기본값'); // '기본값'
console.log(0 ?? '기본값'); // 0 (0은 null/undefined가 아님)
console.log('' ?? '기본값'); // '' (빈 문자열도 그대로)
// Optional Chaining (?., ES2020): null/undefined면 undefined 반환
const obj = { a: { b: { c: 42 } } };
console.log(obj?.a?.b?.c); // 42
console.log(obj?.x?.y?.z); // undefined (에러 없음)
// 스프레드 / 나머지
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
function sum(...nums) { // 나머지 매개변수
return nums.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
JavaScript
복사
4. 제어문
// if-else
const score = 85;
if (score >= 90) {
console.log('A');
} else if (score >= 80) {
console.log('B'); // 출력
} else {
console.log('F');
}
// 삼항 연산자
const grade = score >= 60 ? '합격' : '불합격';
// switch
const day = 'MON';
switch (day) {
case 'SAT':
case 'SUN':
console.log('주말');
break;
default:
console.log('평일'); // 출력
}
// for
for (let i = 0; i < 5; i++) {
process.stdout.write(i + ' '); // 0 1 2 3 4
}
// for...of: 이터러블 순회 (배열, 문자열, Map, Set)
const fruits = ['사과', '바나나', '딸기'];
for (const fruit of fruits) {
console.log(fruit);
}
// for...in: 객체 키 순회 (프로토타입 체인까지 탐색하므로 주의)
const person = { name: '홍길동', age: 30 };
for (const key in person) {
if (Object.hasOwn(person, key)) { // 자신의 속성만
console.log(`${key}: ${person[key]}`);
}
}
// while / do-while
let i = 0;
while (i < 3) {
console.log(i++);
}
JavaScript
복사
5. 함수
5-1. 선언 방식 비교
// 함수 선언식: 호이스팅 O (선언 전에 호출 가능)
function greet(name) {
return `안녕하세요, ${name}!`;
}
// 함수 표현식: 호이스팅 X
const greet2 = function(name) {
return `Hi, ${name}!`;
};
// 화살표 함수 (Arrow Function, ES2015)
// - this를 바인딩하지 않음 (렉시컬 this)
// - 생성자로 사용 불가
// - arguments 객체 없음
const greet3 = (name) => `Hello, ${name}!`;
const double = n => n * 2; // 매개변수 하나면 괄호 생략
const getObj = () => ({ key: 1 }); // 객체 반환 시 괄호 필요
console.log(greet('길동')); // 안녕하세요, 길동!
console.log(double(5)); // 10
JavaScript
복사
5-2. 매개변수
// 기본값 매개변수 (ES2015)
function createUser(name, role = 'user', active = true) {
return { name, role, active };
}
console.log(createUser('홍길동')); // { name: '홍길동', role: 'user', active: true }
console.log(createUser('관리자', 'admin')); // { name: '관리자', role: 'admin', active: true }
// 나머지 매개변수
function logAll(first, ...rest) {
console.log('첫 번째:', first);
console.log('나머지:', rest);
}
logAll(1, 2, 3, 4); // 첫 번째: 1 / 나머지: [2, 3, 4]
JavaScript
복사
5-3. 클로저 (Closure)
함수가 생성될 때의 외부 변수를 기억하는 메커니즘이다.
function makeCounter(start = 0) {
let count = start; // 클로저가 기억하는 변수
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count,
};
}
const counter = makeCounter(10);
console.log(counter.increment()); // 11
console.log(counter.increment()); // 12
console.log(counter.decrement()); // 11
console.log(counter.getCount()); // 11
// count 변수는 외부에서 직접 접근 불가 → 캡슐화
// 실용 예: 메모이제이션
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const expensiveFn = memoize((n) => {
console.log('계산 중...');
return n * n;
});
console.log(expensiveFn(10)); // 계산 중... → 100
console.log(expensiveFn(10)); // (캐시) → 100
JavaScript
복사
6. 객체 (Object)
6-1. 기본 조작
const user = {
name: '홍길동',
age: 30,
'my-key': '특수문자 키', // 문자열 키
greet() { // 메서드 단축 문법
return `안녕, 나는 ${this.name}`;
},
};
// 접근
console.log(user.name); // 점 표기법
console.log(user['my-key']); // 대괄호 표기법 (동적 키 접근)
// 추가 / 수정 / 삭제
user.email = 'test@test.com';
user.age = 31;
delete user['my-key'];
// 존재 확인
console.log('name' in user); // true
console.log(user.hasOwnProperty('email')); // true
// 키 / 값 / 엔트리
console.log(Object.keys(user)); // ['name', 'age', 'email', 'greet']
console.log(Object.values(user)); // ['홍길동', 31, 'test@test.com', f]
console.log(Object.entries(user)); // [['name','홍길동'], ...]
JavaScript
복사
6-2. 구조 분해 할당 (Destructuring)
// 객체 구조 분해
const { name, age, city = '서울' } = user; // 기본값 지정 가능
console.log(name, age, city); // 홍길동 31 서울
// 이름 변경
const { name: userName } = user;
console.log(userName); // 홍길동
// 중첩 객체
const { address: { street } = {} } = { address: { street: '강남대로' } };
console.log(street); // 강남대로
// 배열 구조 분해
const [first, second, ...rest] = [10, 20, 30, 40, 50];
console.log(first, second, rest); // 10 20 [30, 40, 50]
// 함수 매개변수에서 바로 분해
function display({ name, age }) {
console.log(`${name} (${age}세)`);
}
display(user); // 홍길동 (31세)
JavaScript
복사
6-3. 객체 복사와 병합
// 얕은 복사
const copy1 = { ...user }; // 스프레드
const copy2 = Object.assign({}, user); // Object.assign
// 병합 (뒤 객체가 우선)
const merged = { ...user, age: 99, role: 'admin' };
// 깊은 복사 (중첩 객체까지)
const deep = JSON.parse(JSON.stringify(user)); // 함수, undefined, Symbol 제외
const deep2 = structuredClone(user); // ES2022: 더 안전한 방법
JavaScript
복사
7. 배열 (Array)
7-1. 기본 조작
const arr = [1, 2, 3, 4, 5];
// 추가 / 제거
arr.push(6); // 뒤에 추가 → [1,2,3,4,5,6]
arr.pop(); // 뒤에서 제거 → 6 반환
arr.unshift(0); // 앞에 추가 → [0,1,2,3,4,5]
arr.shift(); // 앞에서 제거 → 0 반환
// splice: 임의 위치 삽입/삭제
arr.splice(2, 1, 99); // 인덱스 2에서 1개 삭제 후 99 삽입
console.log(arr); // [1, 2, 99, 4, 5]
// slice: 잘라내기 (원본 불변)
const sliced = arr.slice(1, 3); // [2, 99]
// 검색
console.log(arr.indexOf(99)); // 2
console.log(arr.includes(99)); // true
console.log(arr.find(n => n > 3)); // 4
console.log(arr.findIndex(n => n > 3)); // 3
JavaScript
복사
7-2. 고차 함수 (Higher-Order Function)
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map: 각 요소를 변환한 새 배열 반환
const doubled = nums.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// filter: 조건을 만족하는 요소만 추출
const evens = nums.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// reduce: 누산기로 단일 값 생성
const sum = nums.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 55
// 체이닝
const result = nums
.filter(n => n % 2 === 0) // [2, 4, 6, 8, 10]
.map(n => n ** 2) // [4, 16, 36, 64, 100]
.reduce((a, b) => a + b, 0); // 220
console.log(result); // 220
// forEach: 반환값 없음, 부수효과 목적
nums.forEach((n, idx) => {
if (idx < 3) console.log(`[${idx}] ${n}`);
});
// some: 하나라도 조건 만족하면 true
console.log(nums.some(n => n > 9)); // true
// every: 모두 조건 만족하면 true
console.log(nums.every(n => n > 0)); // true
// flat / flatMap
const nested = [[1, 2], [3, 4], [5]];
console.log(nested.flat()); // [1, 2, 3, 4, 5]
const sentences = ['hello world', 'foo bar'];
console.log(sentences.flatMap(s => s.split(' ')));
// ['hello', 'world', 'foo', 'bar']
// 정렬 (주의: 기본 정렬은 문자열 기준)
const nums2 = [10, 1, 21, 2];
console.log(nums2.sort()); // [1, 10, 2, 21] ← 문자열 정렬
console.log(nums2.sort((a, b) => a - b)); // [1, 2, 10, 21] ← 숫자 정렬
JavaScript
복사
8. this 바인딩
JavaScript에서 this는 호출 방식에 따라 값이 달라진다.
// 1. 일반 함수: 전역 객체 (브라우저: window, Node.js: global)
function showThis() {
console.log(this);
}
showThis(); // window (strict mode에서는 undefined)
// 2. 메서드: 호출한 객체
const obj = {
name: '객체',
show() { console.log(this.name); },
};
obj.show(); // '객체'
// 문제 상황: 메서드를 변수에 담으면 this가 사라짐
const fn = obj.show;
fn(); // undefined (window.name)
// 3. 화살표 함수: 렉시컬 this (선언된 위치의 this)
const obj2 = {
name: '객체2',
greet: () => console.log(this.name), // 화살표: this = 상위 스코프 (window)
greetOk() {
const inner = () => console.log(this.name); // 여기서는 obj2
inner();
},
};
obj2.greet(); // undefined
obj2.greetOk(); // '객체2'
// 4. call / apply / bind: this 명시적 지정
function introduce(greeting, ending) {
return `${greeting}, 나는 ${this.name}${ending}`;
}
const me = { name: '길동' };
console.log(introduce.call(me, '안녕', '!')); // 안녕, 나는 길동!
console.log(introduce.apply(me, ['Hi', '~'])); // Hi, 나는 길동~
const boundFn = introduce.bind(me, '헬로'); // this + 첫 인자 고정
console.log(boundFn('?')); // 헬로, 나는 길동?
JavaScript
복사
9. 프로토타입과 클래스
9-1. 프로토타입 체인
// JS의 모든 객체는 프로토타입 체인을 통해 상위 객체의 속성을 상속
const arr = [1, 2, 3];
// arr → Array.prototype → Object.prototype → null
console.log(arr.hasOwnProperty('length')); // true (자신의 속성)
console.log(arr.hasOwnProperty('map')); // false (Array.prototype에 있음)
JavaScript
복사
9-2. 클래스 (ES2015+)
class Animal {
#name; // private 필드 (ES2022)
constructor(name, sound) {
this.#name = name;
this.sound = sound;
}
speak() {
return `${this.#name}이(가) ${this.sound} 소리를 냅니다.`;
}
get name() { return this.#name; } // getter
static create(name, sound) { // 정적 메서드
return new Animal(name, sound);
}
}
class Dog extends Animal {
#breed;
constructor(name, breed) {
super(name, '멍멍'); // 부모 생성자 호출
this.#breed = breed;
}
speak() {
return super.speak() + ` (품종: ${this.#breed})`;
}
fetch() {
return `${this.name}이(가) 공을 가져옵니다.`;
}
}
const dog = new Dog('바둑이', '진돗개');
console.log(dog.speak()); // 바둑이이(가) 멍멍 소리를 냅니다. (품종: 진돗개)
console.log(dog.fetch()); // 바둑이이(가) 공을 가져옵니다.
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
JavaScript
복사
10. 비동기 처리
10-1. 콜백 (Callback)
// 비동기 작업의 가장 원초적인 형태
setTimeout(() => {
console.log('1초 후 실행');
}, 1000);
// 콜백 지옥: 중첩이 깊어지면 가독성이 급격히 나빠짐
getUser(1, (user) => {
getOrders(user.id, (orders) => {
getProduct(orders[0].productId, (product) => {
console.log(product.name); // 콜백 지옥
});
});
});
JavaScript
복사
10-2. Promise
// Promise: 비동기 작업의 최종 완료 또는 실패를 나타내는 객체
function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url) {
resolve({ data: '응답 데이터' }); // 성공
} else {
reject(new Error('URL이 없음')); // 실패
}
}, 1000);
});
}
// then / catch / finally 체이닝
fetchData('https://api.example.com')
.then(res => {
console.log(res.data);
return '가공된 데이터';
})
.then(processed => console.log(processed))
.catch(err => console.error(err.message))
.finally(() => console.log('완료'));
// Promise 유틸리티
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.reject(new Error('실패'));
// all: 모두 성공 시 배열로, 하나라도 실패하면 즉시 reject
Promise.all([p1, p2])
.then(([v1, v2]) => console.log(v1, v2)); // 1 2
// allSettled: 성공/실패 관계없이 모두 기다림
Promise.allSettled([p1, p2, p3])
.then(results => results.forEach(r => console.log(r.status)));
// fulfilled / fulfilled / rejected
// race: 가장 먼저 완료된 결과 반환
// any: 가장 먼저 성공한 결과 반환 (모두 실패 시 AggregateError)
JavaScript
복사
10-3. async / await
// async 함수는 항상 Promise를 반환
async function getUser(id) {
return { id, name: '홍길동' }; // Promise.resolve({ id, name }) 와 동일
}
async function processOrder(userId) {
try {
const user = await getUser(userId); // Promise 해결 대기
const orders = await fetchOrders(user.id); // 순차 실행
console.log(`${user.name}의 주문: ${orders.length}건`);
} catch (err) {
console.error('에러:', err.message);
} finally {
console.log('처리 완료');
}
}
// 병렬 실행: await를 따로 쓰면 순차, Promise.all로 묶으면 병렬
async function parallel() {
// BAD: 순차 실행 (총 2초)
const a = await fetchA(); // 1초
const b = await fetchB(); // 1초
// GOOD: 병렬 실행 (총 1초)
const [a2, b2] = await Promise.all([fetchA(), fetchB()]);
}
// 반복문에서의 async
async function processAll(items) {
// for...of는 await 사용 가능
for (const item of items) {
await processItem(item);
}
// forEach는 await가 동작하지 않음 (주의)
// items.forEach(async item => await processItem(item)); // 안 됨
}
JavaScript
복사
11. 이터러블과 제너레이터
11-1. 이터러블 (Iterable)
// Symbol.iterator를 구현하면 for...of, 스프레드, 구조분해에 사용 가능
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
},
};
},
};
for (const num of range) {
process.stdout.write(num + ' '); // 1 2 3 4 5
}
console.log([...range]); // [1, 2, 3, 4, 5]
JavaScript
복사
11-2. 제너레이터 (Generator)
// function*: yield로 값을 하나씩 반환
function* idGenerator() {
let id = 1;
while (true) {
yield id++; // 여기서 실행이 일시 중단됨
}
}
const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// 실용 예: 무한 스크롤 페이지네이션
function* paginator(fetchFn, pageSize = 10) {
let page = 1;
while (true) {
const data = yield fetchFn(page, pageSize);
if (!data || data.length < pageSize) return; // 마지막 페이지
page++;
}
}
JavaScript
복사
12. 모듈 시스템
// --- math.js ---
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default class Calculator {
multiply(a, b) { return a * b; }
}
// --- main.js ---
import Calculator, { PI, add } from './math.js'; // named + default
import * as Math from './math.js'; // 전체 임포트
import { add as plus } from './math.js'; // 별칭
const calc = new Calculator();
console.log(PI); // 3.14159
console.log(add(1, 2)); // 3
console.log(calc.multiply(3, 4)); // 12
// 동적 임포트: 코드 스플리팅, 조건부 로드
async function loadModule() {
const { add: dynamicAdd } = await import('./math.js');
console.log(dynamicAdd(10, 20)); // 30
}
JavaScript
복사
13. 에러 처리
// 기본 try-catch-finally
try {
JSON.parse('잘못된 JSON');
} catch (err) {
console.error(err instanceof SyntaxError); // true
console.error(err.name); // SyntaxError
console.error(err.message); // Unexpected token ...
} finally {
console.log('항상 실행');
}
// 커스텀 에러
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = 'AppError';
this.code = code;
}
}
class NetworkError extends AppError {
constructor(statusCode, message) {
super(message, 'NETWORK_ERROR');
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
function fetchApi(url) {
if (!url) throw new AppError('URL 없음', 'INVALID_INPUT');
// 서버 에러 시
throw new NetworkError(404, '리소스를 찾을 수 없음');
}
try {
fetchApi(null);
} catch (err) {
if (err instanceof NetworkError) {
console.log(`네트워크 에러 ${err.statusCode}:`, err.message);
} else if (err instanceof AppError) {
console.log(`앱 에러 [${err.code}]:`, err.message);
} else {
throw err; // 예상 못 한 에러는 다시 던짐
}
}
JavaScript
복사
14. 고급 패턴
14-1. 구조 분해 + 스프레드 실전 패턴
// 객체에서 특정 키 제외한 나머지 추출
const { password, ...safeUser } = { name: '길동', age: 30, password: '1234' };
console.log(safeUser); // { name: '길동', age: 30 }
// 함수 반환값 교환 없이 스왑
let [a, b] = [1, 2];
[a, b] = [b, a];
console.log(a, b); // 2 1
JavaScript
복사
14-2. Map과 Set
// Map: 어떤 타입이든 키로 사용 가능, 삽입 순서 유지
const map = new Map();
map.set('name', '홍길동');
map.set(42, '숫자 키');
map.set({ id: 1 }, '객체 키');
console.log(map.get('name')); // 홍길동
console.log(map.size); // 3
map.delete('name');
for (const [key, value] of map) {
console.log(key, value);
}
// Set: 중복 없는 값 컬렉션
const set = new Set([1, 2, 3, 2, 1]);
console.log(set.size); // 3
set.add(4);
set.delete(2);
console.log([...set]); // [1, 3, 4]
// 중복 제거 실전
const unique = [...new Set([1, 1, 2, 2, 3])];
console.log(unique); // [1, 2, 3]
JavaScript
복사
14-3. Proxy와 Reflect
// Proxy: 객체 접근을 가로채어 커스텀 동작 정의
const handler = {
get(target, prop) {
console.log(`'${prop}' 읽기`);
return prop in target ? target[prop] : `${prop}는 존재하지 않음`;
},
set(target, prop, value) {
if (typeof value !== 'number') {
throw new TypeError('숫자만 허용');
}
return Reflect.set(target, prop, value);
},
};
const obj = new Proxy({}, handler);
obj.score = 95; // set 가로채기
console.log(obj.score); // '에 읽기' → 95
console.log(obj.name); // 'name는 존재하지 않음'
// obj.name = '문자열'; // TypeError
JavaScript
복사
14-4. WeakMap / WeakRef
// WeakMap: 키가 객체, GC에 의해 키가 수거되면 엔트리도 제거
// → 메모리 누수 없이 객체에 부가 데이터 첨부할 때 사용
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, { name, age }); // 외부에서 직접 접근 불가
}
greet() {
const { name } = privateData.get(this);
return `안녕, 나는 ${name}`;
}
}
const p = new Person('길동', 30);
console.log(p.greet()); // 안녕, 나는 길동
// p가 GC되면 privateData 엔트리도 자동 제거
JavaScript
복사
15. 최신 문법 정리 (ES2020 ~ ES2024)
// Optional Chaining (ES2020)
const user = null;
console.log(user?.profile?.avatar); // undefined (에러 없음)
// Nullish Coalescing (ES2020)
console.log(0 ?? '기본값'); // 0 (0은 null/undefined가 아님)
// Promise.allSettled (ES2020)
const results = await Promise.allSettled([p1, p2, p3]);
// String.replaceAll (ES2021)
console.log('a-b-c'.replaceAll('-', '_')); // 'a_b_c'
// Object.hasOwn (ES2022) — hasOwnProperty 보다 안전
console.log(Object.hasOwn({ a: 1 }, 'a')); // true
// at() — 음수 인덱스 지원 (ES2022)
const arr = [1, 2, 3, 4, 5];
console.log(arr.at(-1)); // 5 (마지막 요소)
console.log(arr.at(-2)); // 4
// Error cause (ES2022)
try {
throw new Error('원인 명시', { cause: '네트워크 타임아웃' });
} catch (e) {
console.log(e.cause); // '네트워크 타임아웃'
}
// Array.toSorted / toReversed / toSpliced (ES2023) — 원본 불변
const sorted = [3, 1, 2].toSorted(); // [1, 2, 3]
const reversed = [1, 2, 3].toReversed(); // [3, 2, 1]
const spliced = [1, 2, 3].toSpliced(1, 1); // [1, 3]
// Object.groupBy (ES2024)
const items = [
{ type: 'fruit', name: '사과' },
{ type: 'veggie', name: '당근' },
{ type: 'fruit', name: '바나나' },
];
const grouped = Object.groupBy(items, item => item.type);
// { fruit: [...], veggie: [...] }
JavaScript
복사
핵심 요약
주제 | 핵심 포인트 |
변수 선언 | const 기본, 재할당 필요 시 let, var 사용 지양 |
타입 | typeof, === 엄격 비교, falsy 값 숙지 |
함수 | 화살표 함수는 this 바인딩 없음, 클로저로 캡슐화 |
객체 | 구조 분해, 스프레드, Object.keys/values/entries |
배열 | map/filter/reduce 체이닝, 원본 불변 메서드 선호 |
this | 호출 방식에 따라 달라짐, call/apply/bind로 고정 |
비동기 | 콜백 → Promise → async/await 순서로 이해 |
모듈 | named export • default export, 동적 임포트 |
에러 | 커스텀 에러 계층, instanceof로 분기 |
최신 문법 | ?., ??, at(), toSorted(), Object.groupBy |

