@@ -3,12 +3,6 @@ import { StringSet } from './globals';
3
3
4
4
const BOXABLE_TYPES = new StringSet ( [ 'boolean' , 'number' , 'string' ] ) ;
5
5
6
- // Memory for previously seen containers (object, array, map, set).
7
- // Used for recursion detection, and to avoid repeated comparison.
8
- //
9
- // Elements are { a: val, b: val }.
10
- let memory = [ ] ;
11
-
12
6
function useStrictEquality ( a , b ) {
13
7
return a === b ;
14
8
}
@@ -69,14 +63,14 @@ const objTypeCallbacks = {
69
63
// identical reference only
70
64
function : useStrictEquality ,
71
65
72
- array ( a , b ) {
66
+ array ( a , b , memory ) {
73
67
if ( a . length !== b . length ) {
74
68
// Safe and faster
75
69
return false ;
76
70
}
77
71
78
72
for ( let i = 0 ; i < a . length ; i ++ ) {
79
- if ( ! typeEquiv ( a [ i ] , b [ i ] ) ) {
73
+ if ( ! typeEquiv ( a [ i ] , b [ i ] , memory ) ) {
80
74
return false ;
81
75
}
82
76
}
@@ -88,7 +82,7 @@ const objTypeCallbacks = {
88
82
// repetitions are not counted, so these are equivalent:
89
83
// a = new Set( [ X={}, Y=[], Y ] );
90
84
// b = new Set( [ Y, X, X ] );
91
- set ( a , b ) {
85
+ set ( a , b , memory ) {
92
86
if ( a . size !== b . size ) {
93
87
// This optimization has certain quirks because of the lack of
94
88
// repetition counting. For instance, adding the same
@@ -116,14 +110,11 @@ const objTypeCallbacks = {
116
110
return ;
117
111
}
118
112
119
- // Swap out the global memory, as nested typeEquiv() would clobber it
120
- const originalMemory = memory ;
121
- memory = [ ] ;
113
+ // Don't pass memory, as this nested typeEquiv() must operate
114
+ // independently and would otherwise clobber it
122
115
if ( typeEquiv ( bVal , aVal ) ) {
123
116
innerEq = true ;
124
117
}
125
- // Restore
126
- memory = originalMemory ;
127
118
} ) ;
128
119
129
120
if ( ! innerEq ) {
@@ -168,14 +159,11 @@ const objTypeCallbacks = {
168
159
return ;
169
160
}
170
161
171
- // Swap out the global memory, as nested typeEquiv() would clobber it
172
- const originalMemory = memory ;
173
- memory = [ ] ;
174
- if ( objTypeCallbacks . array ( [ bVal , bKey ] , [ aVal , aKey ] ) ) {
162
+ // Don't memory main memory, as nested typeEquiv() would clobber it
163
+ const localMemory = [ ] ;
164
+ if ( objTypeCallbacks . array ( [ bVal , bKey ] , [ aVal , aKey ] , localMemory ) ) {
175
165
innerEq = true ;
176
166
}
177
- // Restore
178
- memory = originalMemory ;
179
167
} ) ;
180
168
181
169
if ( ! innerEq ) {
@@ -200,7 +188,7 @@ const entryTypeCallbacks = {
200
188
symbol : useStrictEquality ,
201
189
202
190
function : useStrictEquality ,
203
- object ( a , b ) {
191
+ object ( a , b , memory ) {
204
192
// Handle memory (skip recursion)
205
193
if ( memory . some ( ( pair ) => pair . a === a && pair . b === b ) ) {
206
194
return true ;
@@ -212,7 +200,7 @@ const entryTypeCallbacks = {
212
200
if ( aObjType !== 'object' || bObjType !== 'object' ) {
213
201
// Handle literal `null`
214
202
// Handle: Array, Map/Set, Date, Regxp/Function, boxed primitives
215
- return aObjType === bObjType && objTypeCallbacks [ aObjType ] ( a , b ) ;
203
+ return aObjType === bObjType && objTypeCallbacks [ aObjType ] ( a , b , memory ) ;
216
204
}
217
205
218
206
// NOTE: Literal null must not make it here as it would throw
@@ -238,7 +226,7 @@ const entryTypeCallbacks = {
238
226
) {
239
227
continue ;
240
228
}
241
- if ( ! typeEquiv ( a [ i ] , b [ i ] ) ) {
229
+ if ( ! typeEquiv ( a [ i ] , b [ i ] , memory ) ) {
242
230
return false ;
243
231
}
244
232
}
@@ -248,11 +236,16 @@ const entryTypeCallbacks = {
248
236
bProperties . push ( i ) ;
249
237
}
250
238
251
- return objTypeCallbacks . array ( aProperties . sort ( ) , bProperties . sort ( ) ) ;
239
+ return objTypeCallbacks . array ( aProperties . sort ( ) , bProperties . sort ( ) , memory ) ;
252
240
}
253
241
} ;
254
242
255
- function typeEquiv ( a , b ) {
243
+
244
+ // Memory for previously seen containers (object, array, map, set).
245
+ // Used for recursion detection, and to avoid repeated comparison.
246
+ //
247
+ // Elements are { a: val, b: val }.
248
+ function typeEquiv ( a , b , memory = [ ] ) {
256
249
// Optimization: Only perform type-specific comparison when pairs are not strictly equal.
257
250
if ( a === b ) {
258
251
return true ;
@@ -267,14 +260,7 @@ function typeEquiv (a, b) {
267
260
( bType === 'object' && BOXABLE_TYPES . has ( objectType ( b ) ) ? b . valueOf ( ) : b ) ;
268
261
}
269
262
270
- return entryTypeCallbacks [ aType ] ( a , b ) ;
271
- }
272
-
273
- function innerEquiv ( a , b ) {
274
- const res = typeEquiv ( a , b ) ;
275
- // Release any retained objects and reset recursion detection for next call
276
- memory = [ ] ;
277
- return res ;
263
+ return entryTypeCallbacks [ aType ] ( a , b , memory ) ;
278
264
}
279
265
280
266
/**
@@ -285,14 +271,14 @@ function innerEquiv (a, b) {
285
271
*/
286
272
export default function equiv ( a , b ) {
287
273
if ( arguments . length === 2 ) {
288
- return ( a === b ) || innerEquiv ( a , b ) ;
274
+ return ( a === b ) || typeEquiv ( a , b ) ;
289
275
}
290
276
291
277
// Given 0 or 1 arguments, just return true (nothing to compare).
292
278
// Given (A,B,C,D) compare C,D then B,C then A,B.
293
279
let i = arguments . length - 1 ;
294
280
while ( i > 0 ) {
295
- if ( ! innerEquiv ( arguments [ i - 1 ] , arguments [ i ] ) ) {
281
+ if ( ! typeEquiv ( arguments [ i - 1 ] , arguments [ i ] ) ) {
296
282
return false ;
297
283
}
298
284
i -- ;
0 commit comments