99 DatePrototypeGetTime,
1010 Error,
1111 NumberPrototypeValueOf,
12+ ObjectGetOwnPropertyDescriptor,
1213 ObjectGetOwnPropertySymbols : getOwnSymbols ,
1314 ObjectGetPrototypeOf,
1415 ObjectIs,
@@ -192,7 +193,10 @@ function innerDeepEqual(val1, val2, mode, memos) {
192193 typeof val1 !== 'object' ||
193194 val1 === null ||
194195 val2 === null ||
195- ( mode === kStrict && ObjectGetPrototypeOf ( val1 ) !== ObjectGetPrototypeOf ( val2 ) ) ) {
196+ ( mode === kStrict &&
197+ ( val1 . constructor !== val2 . constructor ||
198+ ( val1 . constructor === undefined &&
199+ ObjectGetPrototypeOf ( val1 ) !== ObjectGetPrototypeOf ( val2 ) ) ) ) ) {
196200 return false ;
197201 }
198202 } else {
@@ -316,6 +320,10 @@ function innerDeepEqual(val1, val2, mode, memos) {
316320 isNativeError ( val2 ) ||
317321 val2 instanceof Error ) {
318322 return false ;
323+ } else if ( isURL ( val1 ) ) {
324+ if ( ! isURL ( val2 ) || val1 . href !== val2 . href ) {
325+ return false ;
326+ }
319327 } else if ( isKeyObject ( val1 ) ) {
320328 if ( ! isKeyObject ( val2 ) || ! val1 . equals ( val2 ) ) {
321329 return false ;
@@ -332,10 +340,6 @@ function innerDeepEqual(val1, val2, mode, memos) {
332340 }
333341 } else if ( isWeakMap ( val1 ) || isWeakSet ( val1 ) ) {
334342 return false ;
335- } else if ( isURL ( val1 ) ) {
336- if ( ! isURL ( val2 ) || val1 . href !== val2 . href ) {
337- return false ;
338- }
339343 }
340344
341345 return keyCheck ( val1 , val2 , mode , memos , kNoIterator ) ;
@@ -345,6 +349,21 @@ function getEnumerables(val, keys) {
345349 return ArrayPrototypeFilter ( keys , ( key ) => hasEnumerable ( val , key ) ) ;
346350}
347351
352+ function partialSymbolEquiv ( val1 , val2 , keys2 ) {
353+ const symbolKeys = getOwnSymbols ( val2 ) ;
354+ if ( symbolKeys . length !== 0 ) {
355+ for ( const key of symbolKeys ) {
356+ if ( hasEnumerable ( val2 , key ) ) {
357+ if ( ! hasEnumerable ( val1 , key ) ) {
358+ return false ;
359+ }
360+ ArrayPrototypePush ( keys2 , key ) ;
361+ }
362+ }
363+ }
364+ return true ;
365+ }
366+
348367function keyCheck ( val1 , val2 , mode , memos , iterationType , keys2 ) {
349368 // For all remaining Object pairs, including Array, objects and Maps,
350369 // equivalence is determined by having:
@@ -358,31 +377,15 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) {
358377 if ( keys2 === undefined ) {
359378 keys2 = ObjectKeys ( val2 ) ;
360379 }
361-
362- // Cheap key test
363- if ( keys2 . length > 0 ) {
364- for ( const key of keys2 ) {
365- if ( ! hasEnumerable ( val1 , key ) ) {
366- return false ;
367- }
368- }
369- }
380+ let keys1 ;
370381
371382 if ( ! isArrayLikeObject ) {
372383 // The pair must have the same number of owned properties.
373384 if ( mode === kPartial ) {
374- const symbolKeys = getOwnSymbols ( val2 ) ;
375- if ( symbolKeys . length !== 0 ) {
376- for ( const key of symbolKeys ) {
377- if ( hasEnumerable ( val2 , key ) ) {
378- if ( ! hasEnumerable ( val1 , key ) ) {
379- return false ;
380- }
381- ArrayPrototypePush ( keys2 , key ) ;
382- }
383- }
385+ if ( ! partialSymbolEquiv ( val1 , val2 , keys2 ) ) {
386+ return false ;
384387 }
385- } else if ( keys2 . length !== ObjectKeys ( val1 ) . length ) {
388+ } else if ( keys2 . length !== ( keys1 = ObjectKeys ( val1 ) ) . length ) {
386389 return false ;
387390 } else if ( mode === kStrict ) {
388391 const symbolKeysA = getOwnSymbols ( val1 ) ;
@@ -431,7 +434,7 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) {
431434 d : undefined ,
432435 deep : false ,
433436 } ;
434- return objEquiv ( val1 , val2 , mode , keys2 , memos , iterationType ) ;
437+ return objEquiv ( val1 , val2 , mode , keys1 , keys2 , memos , iterationType ) ;
435438 }
436439
437440 if ( memos . set === undefined ) {
@@ -442,7 +445,7 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) {
442445 memos . c = val1 ;
443446 memos . d = val2 ;
444447 memos . deep = true ;
445- const result = objEquiv ( val1 , val2 , mode , keys2 , memos , iterationType ) ;
448+ const result = objEquiv ( val1 , val2 , mode , keys1 , keys2 , memos , iterationType ) ;
446449 memos . deep = false ;
447450 return result ;
448451 }
@@ -462,7 +465,7 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) {
462465 return true ;
463466 }
464467
465- const areEq = objEquiv ( val1 , val2 , mode , keys2 , memos , iterationType ) ;
468+ const areEq = objEquiv ( val1 , val2 , mode , keys1 , keys2 , memos , iterationType ) ;
466469
467470 set . delete ( val1 ) ;
468471 set . delete ( val2 ) ;
@@ -578,16 +581,34 @@ function setEquiv(a, b, mode, memo) {
578581 // This is a lazily initiated Set of entries which have to be compared
579582 // pairwise.
580583 let set = null ;
581- for ( const val of b ) {
584+ const iteratorB = b . values ( ) ;
585+ for ( const val of iteratorB ) {
582586 if ( ! a . has ( val ) ) {
583587 if ( ( typeof val !== 'object' || val === null ) &&
584588 ( mode !== kLoose || ! setMightHaveLoosePrim ( a , b , val ) ) ) {
585589 return false ;
586590 }
587591
588592 if ( set === null ) {
589- if ( a . size === 1 ) {
590- return innerDeepEqual ( a . values ( ) . next ( ) . value , val , mode , memo ) ;
593+ if ( a . size < 3 ) {
594+ const iteratorA = a . values ( ) ;
595+ const firstA = iteratorA . next ( ) . value ;
596+ const first = innerDeepEqual ( firstA , val , mode , memo ) ;
597+ if ( first ) {
598+ if ( b . size === 1 ) { // Partial mode && a.size === 1
599+ return true ;
600+ }
601+ const secondA = iteratorA . next ( ) . value ;
602+ return b . has ( secondA ) || innerDeepEqual ( secondA , iteratorB . next ( ) . value , mode , memo ) ;
603+ }
604+ if ( a . size === 1 ) {
605+ return false ;
606+ }
607+ return innerDeepEqual ( iteratorA . next ( ) . value , val , mode , memo ) && (
608+ b . size === 1 || // Partial mode
609+ b . has ( firstA ) || // Primitive or reference equal
610+ innerDeepEqual ( firstA , iteratorB . next ( ) . value , mode , memo )
611+ ) ;
591612 }
592613 set = new SafeSet ( ) ;
593614 }
@@ -767,11 +788,28 @@ function sparseArrayEquiv(a, b, mode, memos, i) {
767788 return true ;
768789}
769790
770- function objEquiv ( a , b , mode , keys2 , memos , iterationType ) {
791+ function objEquiv ( a , b , mode , keys1 , keys2 , memos , iterationType ) {
771792 // The pair must have equivalent values for every corresponding key.
772793 if ( keys2 . length > 0 ) {
773- for ( const key of keys2 ) {
774- if ( ! innerDeepEqual ( a [ key ] , b [ key ] , mode , memos ) ) {
794+ let i = 0 ;
795+ // Ordered keys
796+ if ( keys1 !== undefined ) {
797+ for ( ; i < keys2 . length ; i ++ ) {
798+ const key = keys2 [ i ] ;
799+ if ( keys1 [ i ] !== key ) {
800+ break ;
801+ }
802+ if ( ! innerDeepEqual ( a [ key ] , b [ key ] , mode , memos ) ) {
803+ return false ;
804+ }
805+ }
806+ }
807+ // Unordered keys
808+ for ( ; i < keys2 . length ; i ++ ) {
809+ const key = keys2 [ i ] ;
810+ const descriptor = ObjectGetOwnPropertyDescriptor ( a , key ) ;
811+ if ( ! descriptor ?. enumerable ||
812+ ! innerDeepEqual ( descriptor . value !== undefined ? descriptor . value : a [ key ] , b [ key ] , mode , memos ) ) {
775813 return false ;
776814 }
777815 }
0 commit comments