UNPKG

4.9 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 * @preventMunge
9 * @typechecks
10 */
11
12/* eslint-disable no-extend-native */
13
14'use strict';
15
16const Map = require('Map');
17
18const _shouldPolyfillES6Collection = require('_shouldPolyfillES6Collection');
19const toIterator = require('toIterator');
20
21module.exports = (function(global) {
22 // Since our implementation is spec-compliant for the most part we can safely
23 // delegate to a built-in version if exists and is implemented correctly.
24 // Firefox had gotten a few implementation details wrong across different
25 // versions so we guard against that.
26 // These checks are adapted from es6-shim https://fburl.com/34437854
27 if (!_shouldPolyfillES6Collection('Set')) {
28 return global.Set;
29 }
30
31 /**
32 * == ES6 Set Collection ==
33 *
34 * This module is meant to implement a Set collection as described in chapter
35 * 23.2 of the ES6 specification.
36 *
37 * Set objects are collections of unique values. Where values can be any
38 * JavaScript value.
39 * https://people.mozilla.org/~jorendorff/es6-draft.html#sec-map-objects
40 *
41 * There only two -- rather small -- diviations from the spec:
42 *
43 * 1. The use of frozen objects as keys. @see Map module for more on this.
44 *
45 * 2. The `size` property on a map object is a regular property and not a
46 * computed property on the prototype as described by the spec.
47 * The reason being is that we simply want to support ES3 environments
48 * which doesn't implement computed properties.
49 *
50 * == Usage ==
51 *
52 * var set = new set(iterable);
53 *
54 * set.set(value);
55 * set.has(value); // true
56 * set.delete(value); // true
57 *
58 * var iterator = set.keys();
59 * iterator.next(); // {value: value, done: false}
60 *
61 * var iterator = set.values();
62 * iterator.next(); // {value: value, done: false}
63 *
64 * var iterator = set.entries();
65 * iterator.next(); // {value: [value, value], done: false}
66 *
67 * set.forEach(function(value, value){ this === thisArg }, thisArg);
68 *
69 * set.clear(); // resets set.
70 */
71
72 class Set {
73 /**
74 * 23.2.1.1
75 *
76 * Takes an optional `iterable` (which is basically any object that
77 * implements a Symbol.iterator (@@iterator) method). That is a collection
78 * of values used to instantiate the set.
79 *
80 * @param {*} iterable
81 */
82 constructor(iterable) {
83 if (
84 this == null ||
85 (typeof this !== 'object' && typeof this !== 'function')
86 ) {
87 throw new TypeError('Wrong set object type.');
88 }
89
90 initSet(this);
91
92 if (iterable != null) {
93 const it = toIterator(iterable);
94 let next;
95 while (!(next = it.next()).done) {
96 this.add(next.value);
97 }
98 }
99 }
100
101 /**
102 * 23.2.3.1
103 *
104 * If it doesn't already exist in the collection a `value` is added.
105 *
106 * @param {*} value
107 * @return {set}
108 */
109 add(value) {
110 this._map.set(value, value);
111 this.size = this._map.size;
112 return this;
113 }
114
115 /**
116 * 23.2.3.2
117 *
118 * Clears the set.
119 */
120 clear() {
121 initSet(this);
122 }
123
124 /**
125 * 23.2.3.4
126 *
127 * Deletes a `value` from the collection if it exists.
128 * Returns true if the value was found and deleted and false otherwise.
129 *
130 * @param {*} value
131 * @return {boolean}
132 */
133 delete(value) {
134 const ret = this._map.delete(value);
135 this.size = this._map.size;
136 return ret;
137 }
138
139 /**
140 * 23.2.3.5
141 *
142 * Returns an iterator over a collection of [value, value] tuples.
143 */
144 entries() {
145 return this._map.entries();
146 }
147
148 /**
149 * 23.2.3.6
150 *
151 * Iterate over the collection calling `callback` with (value, value, set).
152 *
153 * @param {function} callback
154 */
155 forEach(callback) {
156 const thisArg = arguments[1];
157 const it = this._map.keys();
158 let next;
159 while (!(next = it.next()).done) {
160 callback.call(thisArg, next.value, next.value, this);
161 }
162 }
163
164 /**
165 * 23.2.3.7
166 *
167 * Iterate over the collection calling `callback` with (value, value, set).
168 *
169 * @param {*} value
170 * @return {boolean}
171 */
172 has(value) {
173 return this._map.has(value);
174 }
175
176 /**
177 * 23.2.3.7
178 *
179 * Returns an iterator over the colleciton of values.
180 */
181 values() {
182 return this._map.values();
183 }
184 }
185
186 // 23.2.3.11
187 Set.prototype[toIterator.ITERATOR_SYMBOL] = Set.prototype.values;
188
189 // 23.2.3.7
190 Set.prototype.keys = Set.prototype.values;
191
192 function initSet(set) {
193 set._map = new Map();
194 set.size = set._map.size;
195 }
196
197 return Set;
198})(Function('return this')()); // eslint-disable-line no-new-func