WeakSet
WeakSet
WeakSet는 가비지 컬렉션(garbage collection)이 가능한 값들(객체와 비등록 심볼 포함)을 담는 컬렉션이다. WeakSet 안의 값은 한 번만 존재할 수 있으며, 컬렉션 내에서 유일하다.
본문
개요
WeakSet는 가비지 컬렉트 가능한 값들의 모음이다. 대부분의 원시 데이터 타입은 임의로 생성될 수 있고 수명(lifetime)이 없기 때문에 저장할 수 없다. 반면 객체(object)와 비등록 심볼(symbol)은 가비지 컬렉트가 가능하므로 저장할 수 있다.
Set과의 주요 차이점
일반적인 Set 객체와의 주요 차이점은 다음과 같다:
WeakSet은 객체와 심볼만 담을 수 있는 컬렉션이다.Set이 임의의 타입 값을 담을 수 있는 것과 달리,WeakSet은 어떤 타입의 임의의 값도 담을 수 없다.WeakSet은 "약한(weak)" 참조를 가진다. 즉WeakSet내 객체에 대한 참조는 약하게 유지된다.WeakSet에 저장된 값에 대한 다른 참조가 더 이상 존재하지 않으면, 그 값들은 가비지 컬렉션의 대상이 될 수 있다.
참고: 이는 또한 컬렉션에 저장된 현재 값들의 목록이 존재하지 않는다는 뜻이기도 하다. WeakSet은 열거(enumerable)할 수 없다.
키 동등성(Key equality)
일반 Set과 마찬가지로, 값 동등성은 SameValueZero 알고리즘에 기반한다. WeakSet은 객체와 심볼 값만 담을 수 있기 때문에 이는 === 연산자와 동일하다. 즉, 객체 값에 대해서 동등성은 객체의 정체성(identity)에 기반한다. 값이 아니라 참조(reference)로 비교된다.
생성자(Constructor)
WeakSet()— 새로운WeakSet객체를 생성한다.
인스턴스 속성(Instance properties)
이 속성들은 WeakSet.prototype에 정의되며 모든 WeakSet 인스턴스가 공유한다.
WeakSet.prototype.constructor— 인스턴스 객체를 생성한 생성자 함수.WeakSet인스턴스의 경우 초기 값은WeakSet생성자이다.WeakSet.prototype[Symbol.toStringTag]—[Symbol.toStringTag]속성의 초기 값은 문자열"WeakSet"이다. 이 속성은Object.prototype.toString()에서 사용된다.
인스턴스 메서드(Instance methods)
WeakSet.prototype.add()— 지정된 값을 아직 셋에 존재하지 않으면 이 셋에 삽입한다.WeakSet.prototype.delete()— 지정된 값이 셋에 있으면 이 셋에서 제거한다.WeakSet.prototype.has()— 지정된 값이 이WeakSet에 존재하는지 여부를 나타내는 불리언을 반환한다.
예제
WeakSet 객체 사용하기
const ws = new WeakSet();
const foo = {};
const bar = {};
ws.add(foo);
ws.add(bar);
ws.has(foo); // true
ws.has(bar); // true
ws.delete(foo); // removes foo from the set
ws.has(foo); // false, foo has been removed
ws.has(bar); // true, bar is retained
여기서 foo !== bar라는 점에 주의하자. 두 객체는 유사하지만 동일한 객체는 아니다. 따라서 둘 다 셋에 추가된다.
순환 참조 감지하기(Circular references)
자기 자신을 재귀적으로 호출하는 함수는 어떤 객체가 이미 처리되었는지 추적하여 순환 데이터 구조를 방어할 방법이 필요하다.
WeakSet은 이런 목적에 이상적이다:
// Execute a callback on everything stored inside an object
function execRecursively(fn, subject, _refs = new WeakSet()) {
// Avoid infinite recursion
if (_refs.has(subject)) {
return;
}
fn(subject);
if (typeof subject === "object" && subject) {
_refs.add(subject);
for (const key in subject) {
execRecursively(fn, subject[key], _refs);
}
_refs.delete(subject);
}
}
const foo = {
foo: "Foo",
bar: {
bar: "Bar",
},
};
foo.bar.baz = foo; // Circular reference!
execRecursively((obj) => console.log(obj), foo);
여기서 WeakSet은 첫 실행에서 생성되며, 이후의 모든 함수 호출에 내부 _refs 파라미터를 통해 함께 전달된다.
객체의 수나 탐색 순서는 중요하지 않으므로, 특히 매우 많은 수의 객체가 관여할 때 객체 참조를 추적하는 데는 Set보다 WeakSet이 더 적합하고(성능도 더 좋다) 효율적이다.
명세(Specifications)
- ECMAScript® 2027 Language Specification — sec-weakset-objects
브라우저 호환성
호환성 표는 JavaScript를 활성화해야 볼 수 있다. basline 표시에 따르면 이 기능은 2015년 9월부터 다양한 브라우저에서 널리 사용 가능하다.
참고 자료
WeakSet의core-js폴리필MapSetWeakMap