키 기반 컬렉션

키 기반 컬렉션 (Keyed collections)

이 장에서는 키(key)로 인덱싱되는 데이터 컬렉션을 소개합니다. MapSet 객체는 삽입 순서대로 반복(iterate) 가능한 요소들을 담고 있습니다.

출처: Keyed collections

본문

Maps

Map 객체

Map 객체는 요소를 삽입 순서대로 반복할 수 있는 키/값 맵(key/value map)입니다. 다음 코드는 Map을 사용하는 몇 가지 기본 연산을 보여줍니다. 더 많은 예제와 전체 API는 Map 참조 페이지를 참고하세요. for...of 루프를 사용하면 각 반복마다 [key, value] 배열을 얻을 수 있습니다.

const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // false

for (const [key, value] of sayings) {
  console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"

sayings.clear();
sayings.size; // 0

Object와 Map 비교

전통적으로 객체(Object)는 문자열을 값에 매핑하는 데 사용되었습니다. 객체는 키에 값을 설정하고, 값을 검색하고, 키를 삭제하며, 특정 키에 무언가 저장되었는지 감지할 수 있습니다. 그러나 Map 객체는 더 나은 맵이 되게 하는 몇 가지 추가 장점이 있습니다.

  • Object의 키는 문자열 또는 symbol인 반면, Map의 키는 임의의 값이 될 수 있습니다.
  • Map의 크기(size)를 쉽게 얻을 수 있지만, Object는 크기를 직접 추적해야 합니다.
  • 맵의 반복은 요소의 삽입 순서대로 이루어집니다.
  • Object는 프로토타입을 가지므로 맵에 기본 키들이 존재합니다. (map = Object.create(null)을 사용하면 이를 우회할 수 있습니다.)

다음 세 가지 팁은 MapObject 중 무엇을 사용할지 결정하는 데 도움이 됩니다.

  • 키가 런타임 시점까지 알려지지 않을 때, 특히 키가 외부 입력에서 오는 경우에는 객체보다 맵을 사용하세요.
  • Map은 모든 타입의 키와 값을 지원하며, 키가 문자열이나 symbol로 직렬화 가능할 것을 요구하지 않습니다.
  • 모양(shape)이 미리 정해져 있고 모든 키가 문자열로 표현 가능할 때는 객체를 사용하세요.

WeakMap 객체

WeakMap은 키가 객체 또는 등록되지 않은 symbol이어야 하고, 값은 임의의 JavaScript 타입이 될 수 있으며, 키에 대한 강한 참조(strong reference)를 만들지 않는 키/값 쌍의 컬렉션입니다. 즉, 객체가 WeakMap의 키로 존재한다고 해서 그 객체가 가비지 컬렉션되지 않게 막지는 않습니다. 키로 사용된 객체가 수집되면, 해당 키에 연결된 WeakMap의 값들도 다른 곳에서 강하게 참조되지 않는 한 가비지 컬렉션의 대상이 됩니다. WeakMap 키로 사용할 수 있는 유일한 원시 타입(primitive type)은 symbol, 정확히는 등록되지 않은 symbol뿐입니다. 등록되지 않은 symbol은 고유함이 보장되고 다시 생성될 수 없기 때문입니다.

WeakMap API는 기본적으로 Map API와 동일합니다. 그러나 WeakMap은 키의 생존 여부(liveness)를 관찰하는 것을 허용하지 않으므로 열거(enumeration)를 지원하지 않습니다. 따라서 WeakMap에 있는 키 목록을 얻는 메서드는 없습니다. 만약 그런 메서드가 있다면, 그 목록은 가비지 컬렉션의 상태에 의존하게 되어 비결정성(non-determinism)이 생길 것입니다.

더 많은 정보와 예제 코드는 WeakMap 참조 페이지의 "Why WeakMap?"을 참고하세요.

WeakMap 객체의 한 가지 사용 사례는 객체의 전용(private) 데이터를 저장하거나 구현 세부 사항을 숨기는 것입니다. 다음 예제에서 전용 데이터와 메서드는 객체 내부에 속하며, privates라는 WeakMap에 저장됩니다. 인스턴스와 프로토타입에 노출된 것은 모두 공개적이고, 그 외의 모든 것은 privates가 모듈에서 내보내지지 않으므로 외부에서 접근할 수 없습니다.

const privates = new WeakMap();

export default function Public() {
  const me = {
    // Private data goes here
  };
  privates.set(this, me);
}

Public.prototype.method = function () {
  const me = privates.get(this);
  // Do stuff with private data in `me`
  // …
};

참고: 이 사용 사례는 이제 클래스와 전용 필드(private fields)로 구현할 수 있습니다.

Sets

Set 객체

Set 객체는 고유한 값들의 컬렉션입니다. 요소를 삽입 순서대로 반복할 수 있습니다. Set의 값은 한 번만 존재할 수 있으며, Set 컬렉션 내에서 고유합니다.

다음 코드는 Set을 사용하는 몇 가지 기본 연산을 보여줍니다. 더 많은 예제와 전체 API는 Set 참조 페이지를 참고하세요.

const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");

mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2

for (const item of mySet) {
  console.log(item);
}
// 1
// "some text"

Array와 Set 간 변환

Array.from 또는 스프레드(spread) 문법을 사용해 Set으로부터 Array를 만들 수 있습니다. 또한 Set 생성자는 반대 방향으로 변환하기 위해 Array를 인자로 받습니다.

참고: Set 객체는 고유한 값만 저장하므로, 변환 시 Array의 중복 요소는 삭제됩니다!

Array.from(mySet);
[...mySet2];

mySet2 = new Set([1, 2, 3, 4]);

Array와 Set 비교

전통적으로 많은 상황에서 요소들의 집합은 JavaScript에서 배열에 저장되어 왔습니다. 그러나 Set 객체는 몇 가지 장점이 있습니다.

  • 값으로 배열 요소를 삭제하는 것(arr.splice(arr.indexOf(val), 1))은 매우 느립니다.
  • Set 객체는 값으로 요소를 삭제할 수 있게 해줍니다. 배열에서는 요소의 인덱스를 기준으로 splice해야 합니다.
  • 배열에서 NaN 값은 indexOf로 찾을 수 없습니다.
  • Set 객체는 고유한 값을 저장합니다. 중복을 수동으로 추적할 필요가 없습니다.

WeakSet 객체

WeakSet 객체는 객체와 등록되지 않은 symbol을 포함한 가비지 컬렉션 가능한 값들의 컬렉션입니다. WeakSet의 값은 한 번만 존재할 수 있으며, WeakSet 컬렉션 내에서 고유합니다.

Set 객체와의 주요 차이점은 다음과 같습니다.

  • Set과 달리 WeakSet은 객체 또는 symbol만의 컬렉션이며, 임의의 타입 값의 컬렉션이 아닙니다.
  • WeakSet은 약합니다(weak). 컬렉션에 있는 객체에 대한 참조는 약하게 유지됩니다. WeakSet에 저장된 객체에 대한 다른 참조가 없다면 가비지 컬렉션될 수 있습니다. 이는 또한 컬렉션에 저장된 현재 객체 목록이 없음을 의미합니다.
  • WeakSet은 열거 가능하지 않습니다(enumerable).

WeakSet 객체의 사용 사례는 제한적입니다. 메모리를 누출하지 않으므로, 예를 들어 DOM 요소를 키로 사용해 추적 목적으로 표시하는 데 안전하게 사용할 수 있습니다.

Map과 Set의 키/값 동등성

Map 객체의 키 동등성과 Set 객체의 값 동등성은 모두 SameValueZero 알고리즘에 기반합니다.

  • 동등성은 일치 연산자 ===와 동일하게 동작합니다.
  • -0+0은 동등하게 간주됩니다.
  • NaN은 자기 자신과 동등하게 간주됩니다(===와는 반대).

더 알아보기