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 | ;
|
15 |
|
16 | const Map = require('Map');
|
17 |
|
18 | const _shouldPolyfillES6Collection = require('_shouldPolyfillES6Collection');
|
19 | const toIterator = require('toIterator');
|
20 |
|
21 | module.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
|