[JavaScript]
기초
- [JS] Js기초
- [JS] Js기초 변수
- [JS] Js기초 자료형
- [JS] Js기초 형변환
- [JS] Js기초 연산자
- [JS] Js기초 반복문
- [JS] Js기초 switch
- [JS] Js기초 function
- [JS] Js기초 객체
- [JS] Js기초 배열
중급
- [JS] Js중급 호이스팅(Hoisting)과 TDZ(Temporal Dead Zone)
- [JS] Js중급 생성자함수
- [JS] 객체 메소드(Object methods), 계산된 프로퍼티(Computed property)
- [JS] 심볼(Symbol)
- [JS] WeakMap WeakSet
- [JS] 숫자, 수학 method (Number, Math)
- [JS] 문자열 메소드(String methods)
- [JS] 배열 메소드(Array methods)
- [JS] 구조 분해 할당 (Destructuring assignment)
- [JS] 매개변수 리스트와 전개 문법(Rest parameters and spread syntax)
- [JS] 클로저(Closure)
- [JS] setTimeout / setInterval
- [JS] call / apply / bind
- [JS] 상속, 프로토타입(Prototype)
- [JS] 클래스(Class)
- [JS] 클로미스(Promise)
- [JS] 비동기 처리(Async/Await)
- [JS] Generator
- [JS] 메모리 릭(Memory Leak)
WeakMap은 키가 반드시 객체여야 하고, 참조가 없어지면 가비지 컬렉션의 대상이 되는 특별한 맵이야! 🗑️
1. WeakMap 기본
1
2
3
4
5
6
7
8
9
10
11
12
| // WeakMap 생성
const weakMap = new WeakMap();
// 일반 객체를 키로 사용
let obj = { name: "테스트" };
weakMap.set(obj, "비밀 데이터");
console.log(weakMap.get(obj)); // "비밀 데이터"
// 원시값은 키로 사용할 수 없음!
weakMap.set("문자열", 123); // TypeError!
weakMap.set(123, "숫자"); // TypeError!
|
2. 가비지 컬렉션 동작
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| let user = { name: "김코딩" };
const userInfo = new WeakMap();
// 사용자 정보 저장
userInfo.set(user, {
lastLogin: new Date(),
visits: 1,
});
// user 객체에 대한 참조가 사라지면
user = null;
// userInfo의 데이터도 자동으로 가비지 컬렉션됨
// (바로는 아니고, JS 엔진이 판단하는 적절한 시점에)
|
3. 프라이빗 데이터 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| const privateData = new WeakMap();
class User {
constructor(name, age) {
// 프라이빗 데이터 저장
privateData.set(this, {
name: name,
age: age,
});
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
setAge(newAge) {
privateData.get(this).age = newAge;
}
}
const user = new User("김코딩", 20);
console.log(user.getName()); // "김코딩"
console.log(privateData.get(user)); // { name: "김코딩", age: 20 }
|
4. 캐시/메모이제이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| const cache = new WeakMap();
function processUser(user) {
// 이미 처리된 결과가 있는지 확인
if (cache.has(user)) {
console.log("캐시된 결과 사용!");
return cache.get(user);
}
// 새로운 결과 계산
const result = {
processed: true,
lastUpdate: new Date(),
};
// 결과를 캐시에 저장
cache.set(user, result);
return result;
}
let user = { id: 1, name: "박해커" };
processUser(user); // 계산 수행
processUser(user); // 캐시된 결과 사용
// user 객체가 없어지면 캐시도 자동 정리됨
user = null;
|
5. 이벤트 리스너 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| const listeners = new WeakMap();
class EventEmitter {
constructor() {
listeners.set(this, new Map());
}
on(event, callback) {
let eventMap = listeners.get(this);
if (!eventMap.has(event)) {
eventMap.set(event, new Set());
}
eventMap.get(event).add(callback);
}
emit(event, data) {
const callbacks = listeners.get(this).get(event);
if (callbacks) {
callbacks.forEach((callback) => callback(data));
}
}
}
let emitter = new EventEmitter();
emitter.on("message", (data) => console.log(data));
emitter.emit("message", "안녕하세요!"); // "안녕하세요!"
// emitter가 사라지면 리스너도 자동으로 정리됨
emitter = null;
|
WeakMap vs Map 비교 📊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 1. Map - 모든 타입의 키 사용 가능
const normalMap = new Map();
normalMap.set("문자열", 123);
normalMap.set(123, "숫자도 가능");
// 2. WeakMap - 객체만 키로 사용 가능
const weakMap = new WeakMap();
weakMap.set({ id: 1 }, "객체만 가능");
// weakMap.set("문자열", 123); // 에러!
// 3. Map은 순회 가능
for (let [key, value] of normalMap) {
console.log(key, value);
}
// 4. WeakMap은 순회 불가능
// for (let [key, value] of weakMap) {} // 에러!
|
사용 시기 🤔
WeakMap은 이럴 때 사용하면 좋아:
- 객체와 관련된 프라이빗 데이터 저장
- 캐시나 메모이제이션 구현
- 메모리 누수 방지가 필요할 때
- 객체의 생명주기와 연관된 데이터 관리
1
2
3
4
5
6
7
8
9
10
11
| // 좋은 사용 예
const userSessions = new WeakMap();
function login(user) {
userSessions.set(user, {
loginTime: new Date(),
token: Math.random().toString(36),
});
}
// user 객체가 사라지면 세션 정보도 자동으로 정리됨!
|
이제 WeakMap은 마스터! 😎
메모리 관리가 중요한 상황에서 아주 유용하게 사용할 수 있어!
WeakSet도 WeakMap과 비슷해! 객체만 저장할 수 있고, 참조가 없어지면 자동으로 가비지 컬렉션 돼! 🗑️
1. WeakSet 기본
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // WeakSet 생성
const weakSet = new WeakSet();
// 객체만 추가 가능
let obj = { name: "테스트" };
weakSet.add(obj);
// 원시값은 추가 불가!
weakSet.add(123); // TypeError!
weakSet.add("문자열"); // TypeError!
// 객체 존재 여부 확인
console.log(weakSet.has(obj)); // true
// obj 참조가 사라지면 WeakSet에서도 자동 삭제
obj = null;
|
2. 방문자 추적 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| const visited = new WeakSet();
class Page {
constructor(title) {
this.title = title;
}
visit() {
if (visited.has(this)) {
console.log(`재방문: ${this.title}`);
} else {
visited.add(this);
console.log(`첫 방문: ${this.title}`);
}
}
}
let page1 = new Page("홈페이지");
page1.visit(); // "첫 방문: 홈페이지"
page1.visit(); // "재방문: 홈페이지"
// page1이 null이 되면 visited에서도 자동 삭제
page1 = null;
|
3. 중복 방지 사용 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| const processed = new WeakSet();
class Task {
constructor(name) {
this.name = name;
}
process() {
if (processed.has(this)) {
console.log(`${this.name}: 이미 처리됨!`);
return;
}
console.log(`${this.name} 처리 중...`);
// 처리 로직...
processed.add(this);
console.log(`${this.name} 처리 완료!`);
}
}
const task = new Task("데이터 분석");
task.process(); // "데이터 분석 처리 중..." -> "처리 완료!"
task.process(); // "데이터 분석: 이미 처리됨!"
|
4. WeakSet vs Set 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // 1. Set - 모든 타입의 값 저장 가능
const normalSet = new Set();
normalSet.add(1);
normalSet.add("문자열");
normalSet.add({ id: 1 });
// 2. WeakSet - 객체만 저장 가능
const weakSet = new WeakSet();
weakSet.add({ id: 1 });
// weakSet.add(1); // 에러!
// 3. Set은 순회 가능
for (let item of normalSet) {
console.log(item);
}
// 4. WeakSet은 순회 불가능
// for (let item of weakSet) {} // 에러!
// 5. Set은 크기 확인 가능
console.log(normalSet.size); // 3
// 6. WeakSet은 크기 확인 불가
// console.log(weakSet.size); // undefined
|
5. 실전 예제: 이벤트 한 번만 실행하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| const executedHandlers = new WeakSet();
class EventManager {
constructor() {
this.handlers = [];
}
addHandler(handler) {
this.handlers.push(handler);
}
executeOnce(handler, ...args) {
if (!executedHandlers.has(handler)) {
handler(...args);
executedHandlers.add(handler);
} else {
console.log("이미 실행된 핸들러입니다!");
}
}
}
const manager = new EventManager();
const handler = {
handle: (data) => console.log(`처리: ${data}`),
};
manager.executeOnce(handler, "테스트1"); // "처리: 테스트1"
manager.executeOnce(handler, "테스트2"); // "이미 실행된 핸들러입니다!"
|
사용 시기 🤔
WeakSet은 이럴 때 사용하면 좋아:
- 객체의 중복 실행 방지
- 객체 방문 여부 추적
- 임시 객체 플래그 설정
- 메모리 누수 방지가 필요할 때
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 실제 사용 예: DOM 요소 처리 추적
const processedElements = new WeakSet();
function processElement(element) {
if (processedElements.has(element)) {
console.log("이미 처리된 요소입니다.");
return;
}
// 요소 처리 로직...
processedElements.add(element);
}
// DOM 요소가 삭제되면 WeakSet에서도 자동으로 정리됨!
|
WeakSet vs WeakMap 비교 📊
-
WeakSet
- 객체만 저장
- 값만 저장 (키-값 쌍 없음)
- has, add, delete 메서드만 제공
-
WeakMap
- 객체를 키로 사용
- 키-값 쌍 저장
- get, set, has, delete 메서드 제공
1
2
3
4
5
6
7
| // WeakSet 예제
const visitedPages = new WeakSet();
visitedPages.add(pageObj);
// WeakMap 예제
const pageData = new WeakMap();
pageData.set(pageObj, { visits: 1 });
|
이제 WeakSet도 마스터! 😎
객체 관련 플래그나 상태를 추적할 때 아주 유용하게 사용할 수 있어!