ES2015에는 새로운 자료구조들이 추가 되었다. Map과 Set이다.
Map은 객체와 유사하고 Set은 배열과 유사하다고 생각하면 된다.
Map은 일반 객체리터럴과 비슷 {}
Set은 배열과 비슷 []
자바스크립트 단점이 하나의 기능에 자유도가 너무 높다.
다른 언어는 Map같은 거 하나로 제한되어 있다.
Map은 키와 값이 있는 객체다.
{'a' : 'b'}

const m = new Map;
m.set('a','b') //Map {'a' => 'b'}
m.set('c','d') // {'a' => 'b' , 'c' => 'd'}
신기한 게 키,값을 각각 객체로 줄 수 있다..
일반 객체는 key가 무조건 symbol 아니면 string인데 말이다.
const mapEx = new Map();
mapEx.set = ({ a: "b" }, { c: "d" });
const getMap = mapEx.get({ a: "b" });
console.log(getMap); // undefined
map에서 키로 사용되는 객체는 참조타입(메모리주소 기준)이다.
즉, 동일한 구조의 객체라도 다른 메모리 주소를 가지면 서로 다른 객체로 간주된다.
mapEx.set({a:'b'},{c:'d'}) //첫번째 객체 {a : "b"}
mapEx.get({a:"b"}) // 두번째 객체 {a: "b"} (다른 참조)
이렇게 하면 Map 내부에서 키를 찾을 때, 참조가 다르기 때문에 해당 키를 찾지 못하고 undefined를 반환한다.(메모리주소가 다른 거임)
그래서 어떻게 써야하냐면 한 변수에 담아서 뽑아써야 한다.
const obj = {key : 'key'}
m.set(obj, 123)
m.get(obj)
이러면 메모리주소를 건드리지 않기 때문에 value를 똑같은 값으로 뽑을 수 있다.
그럼 Key값은 어떻게 보나요?
for..of 또는 entries()를 사용해야 한다.
const obj = { key: 'key' };
const m = new Map();
m.set(obj, 123); // obj를 키로 설정
m.set({ another: 'key' }, 456); // 또 다른 객체 키 설정
// 전체 순회
for (const [key, value] of m.entries()) {
console.log('Key:', key, 'Value:', value);
}
Key: { key: 'key' } Value: 123
Key: { another: 'key' } Value: 456
key()로 key만 보기
for(const key of m.keys()){
console.log('key:',key);
}
Key : { key : 'key'}
Key : { another: 'key'}
콘솔로그로 확인
console.log(m);
JSON.stiringfy는 안된다. 객체는 직렬화가 안되기에!
-
직렬화: 배열 같은 데이터 구조를 문자열로 변환하는 과정
length기능은 size다.
그리고 원래 객체는 length못쓴다.
m.size
객체 리터럴 돌릴 때 반복문 돌리기가 너무 힘들었다.
이거 문제가 프로토타입에 속성이 다 나와서 If~ 같은 보호장치를 넣어주었다.
for(let i in obj2){
if(obj2.hasOwnProperty(i)){
obj2[i]
}
}
Map을 쓰면 반복문은 이렇게 쓸 수 있다.(k는 key고 v는 value다.)
for(const [k,v] of m){
console.log(k,v)
}
Map은 배열도 아닌데 forEach도 된다.
m.forEach(v,k) => {
console.log(k,v)
}
들어있냐 확인할 땐 has를 쓰면 된다. delete,
m.has(2)
m.delete(obj) // true
m.has(obj) // false
Set
Set은 배열인데 배열을 완벽하게 대체하기는 힘들다.
특별한 배열이다.
set은 중복을 허용하지 않는 것이 가장 큰 특징이다.
ex)[1,2,3,4,4,3,2,1] 이런거 허용 안된다.
const s = new Set();
s.add(1);
s.add(1);
s.add(2);
console.log("set", s);
// 결과 Set(2) { 1, 2 }
자료형이 다르면 용납한다.
set은 거의 안쓰다 하나 쓰일 때가 있다.
중복을 없앨 때 좋다.
const arr = [1, 2, 3, 2, 3, 5, 2];
const s2 = new Set(arr);
const s3 = Array.from(s2); // 다시 배열로 돌리기
console.log(s3);
WeakMap
맵과 비슷하게 키-값을 쌍으로 저장하지만,
키로 객체만 사용할 수 있고 특정 조건에 따라 메모리가 자동으로 정리(가비지 컬렉션)
특징 : 키는 반드시 객체여야 함
const wm = new WeakMap();
const key = { name: "네임" };
wm.set(key, "ㅎㅎㅇㅇ");
console.log("WeakMap:", wm.get(key));
기본 타입(숫자,문자열)은 키로 못쓴다.
가비지 컬렉션 지원
객체 키가 더 이상 참조되지 않으면 해당 키와 값이 메모리에서 자동으로 삭제됩니다.
let key = { name: "내 이름" };
const wm = new WeakMap();
wm.set(key, "value");
key = null; // key가 더 이상 참조되지 않음
// 이제 메모리에서 자동 삭제 (가비지 컬렉션 실행)
크기 확인 불가
-
WeakMap은 크기나 내용을 직접 확인할 수 없어요(.size, forEach 없음).
WeakSet
weakSet은 Set과 비슷하게 유일한 객체 집합을 저장하지만,마찬가지로 객체만 저장할 수 있고 가비지 컬렉션을 지원한다.
-
객체만 저장 가능
const ws = new WeakSet();
const obj = { name: "이름" };
ws.add(obj);
console.log(ws.has(obj)); // true
-
가비지 컬렉션 지원
let obj = {name : '내이름'}
const ws = new WeakSet();
ws.add(obj);
obj = null; //obj가 더이상 참조되지 않음
//이제 메모리에서 자동 삭제
-
크기 확인 불가WeakSet도 Size나 내용을 직접 확인할 수 없다.
WeakMap,WeakSet 왜 쓰냐면
주로 메모리 관리를 효율적으로 하고 싶을 때 사용된다.
-
캐싱(WeakMap)
키로 객체를 사용해 데이터를 캐싱하고, 객체가 더 이상 필요 없을 때 자동으로 정리
const cache = new WeakMap();
function process(obj) {
if (!cache.has(obj)) {
cache.set(obj, `처리된 데이터: ${obj.name}`);
}
return cache.get(obj);
}
let data = { name: "내이름" };
console.log(process(data)); // 처리된 데이터: 내이름
data = null; // data가 더 이상 참조되지 않으면 캐시에서도 제거
-
DOM 요소 추적(WeakSet)
const seenElements = new WeakSet();
function markAsSeen(element) {
if (seenElements.has(element)) {
console.log("이미 본 요소입니다.");
} else {
seenElements.add(element);
console.log("새로운 요소입니다.");
}
}
const div = document.createElement("div");
markAsSeen(div); // 새로운 요소입니다.
markAsSeen(div); // 이미 본 요소입니다.
특징WeakMapWeakSetMap / Set
키로 사용할 수 있는 값
|
객체만 가능
|
객체만 가능
|
기본 타입과 객체 모두 가능
|
가비지 컬렉션 지원
|
O
|
O
|
X
|
크기 확인 가능 여부
|
X (size 없음)
|
X (size 없음)
|
O (size 속성 있음)
|
순회 가능 여부
|
X (forEach 없음)
|
X (forEach 없음)
|
O (forEach, keys 등 사용 가능)
|
나중에 쓸 일이 있겠지. 현재까지 본 코드중에 Weak시리즈를 본 적이 없다.
(그나마 Set은 좀 쓸 거 같다.)
옵셔널 체이닝, 널 병합
Optional Chaining
객체 속성을 접근할 때 값이 없으면 에러가 나는 것을 방지하는 문법
보통 객체 안에 또 객체가 있을 대 속성을 찾으려고 하면 값이 없어서 에러가 나는데,
옵셔널 체이닝을 사용하면 undefined로 값을 준다.
const obj = { user: { name: '이름임' } };
console.log(obj.user.name); // '이름임'
console.log(obj.profile.name); // ❌ 에러 발생 (Cannot read property 'name' of undefined)
console.log(obj.profile?.name); // undefined
함수 호출에도 사용 가능
const obj = { greet: () => '안녕하세요!' };
console.log(obj.greet?.()); // '안녕하세요!'
console.log(obj.nonExistentMethod?.()); // undefined
Null 병합(Nullish Coalescing)
널 병합은 null이나 undefined인 경우에만 기본값을 설정 해주는 문법이다.
보통 || 연산자를 쓰면 비슷하게 동작하지만, 0 ,'' 같은 falsy값도 처리하기에 문제가 될 수 있었다.
const result = value ?? defaultValue;
- value: 확인할 값.
- ??: 널 병합 연산자.
- defaultValue: value가 null 또는 undefined일 때 사용할 기본값.
널 병합의 작동방식
널 병합은 오직 null 또는 undefined 일때만 기본값을 적용한다.
다른 모든 값(예: 0,false,'')일 땐 그대로 사용한다.
const name = null ?? "기본 이름";
console.log(name); // "기본 이름"
const age = undefined ?? 18;
console.log(age); // 18
const score = 0 ?? 100; // 0은 falsy 값이지만 null/undefined가 아님
console.log(score); // 0
const isLoggedIn = false ?? true;
console.log(isLoggedIn); // false
const text = '' ?? '기본 텍스트';
console.log(text); // ''
널 병합과 ||의 차이
널 병합은 ||와 비슷하지만, ||는 falsy값(0,false,'' 등)을 무시한다.
널 병합은 오직 null/undefined만 기본값을 적용한다.
const username = null;
const displayName = username ?? '손님';
console.log(displayName); // '손님'
요약
- 널 병합 (??):
- null 또는 undefined일 때만 기본값을 사용.
- 다른 falsy 값(0, false, '')은 그대로 유지.
- ||와 비교:
- ||는 falsy 값 전체를 무시하지만, 널 병합은 null/undefined만 무시.
- 사용 사례:
- 기본값 설정.
- 함수 매개변수의 기본값.
- 객체 속성 기본값 지정.
---
+ 추가
뒤에 reading 'd'라는 글이 있는데 이 에러는 d가 에러다 아니라
c.d의 c가 에러인거다.
'Back-End > Node.js' 카테고리의 다른 글
Node.js교과서 PART3 (0) | 2025.02.13 |
---|---|
Node.js 교과서 PART2 (2) (0) | 2025.01.28 |
Node.js 교과서 PART2 (1) (0) | 2025.01.19 |
Node.js 교과서 PART1 (0) | 2025.01.18 |
다시 시작하는 Node.js, 새로 시작하는 CS (0) | 2025.01.11 |