equality.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var _a = Object.prototype, toString = _a.toString, hasOwnProperty = _a.hasOwnProperty;
  4. var previousComparisons = new Map();
  5. /**
  6. * Performs a deep equality check on two JavaScript values, tolerating cycles.
  7. */
  8. function equal(a, b) {
  9. try {
  10. return check(a, b);
  11. }
  12. finally {
  13. previousComparisons.clear();
  14. }
  15. }
  16. function check(a, b) {
  17. // If the two values are strictly equal, our job is easy.
  18. if (a === b) {
  19. return true;
  20. }
  21. // Object.prototype.toString returns a representation of the runtime type of
  22. // the given value that is considerably more precise than typeof.
  23. var aTag = toString.call(a);
  24. var bTag = toString.call(b);
  25. // If the runtime types of a and b are different, they could maybe be equal
  26. // under some interpretation of equality, but for simplicity and performance
  27. // we just return false instead.
  28. if (aTag !== bTag) {
  29. return false;
  30. }
  31. switch (aTag) {
  32. case '[object Array]':
  33. // Arrays are a lot like other objects, but we can cheaply compare their
  34. // lengths as a short-cut before comparing their elements.
  35. if (a.length !== b.length)
  36. return false;
  37. // Fall through to object case...
  38. case '[object Object]': {
  39. if (previouslyCompared(a, b))
  40. return true;
  41. var aKeys = Object.keys(a);
  42. var bKeys = Object.keys(b);
  43. // If `a` and `b` have a different number of enumerable keys, they
  44. // must be different.
  45. var keyCount = aKeys.length;
  46. if (keyCount !== bKeys.length)
  47. return false;
  48. // Now make sure they have the same keys.
  49. for (var k = 0; k < keyCount; ++k) {
  50. if (!hasOwnProperty.call(b, aKeys[k])) {
  51. return false;
  52. }
  53. }
  54. // Finally, check deep equality of all child properties.
  55. for (var k = 0; k < keyCount; ++k) {
  56. var key = aKeys[k];
  57. if (!check(a[key], b[key])) {
  58. return false;
  59. }
  60. }
  61. return true;
  62. }
  63. case '[object Error]':
  64. return a.name === b.name && a.message === b.message;
  65. case '[object Number]':
  66. // Handle NaN, which is !== itself.
  67. if (a !== a)
  68. return b !== b;
  69. // Fall through to shared +a === +b case...
  70. case '[object Boolean]':
  71. case '[object Date]':
  72. return +a === +b;
  73. case '[object RegExp]':
  74. case '[object String]':
  75. return a == "" + b;
  76. case '[object Map]':
  77. case '[object Set]': {
  78. if (a.size !== b.size)
  79. return false;
  80. if (previouslyCompared(a, b))
  81. return true;
  82. var aIterator = a.entries();
  83. var isMap = aTag === '[object Map]';
  84. while (true) {
  85. var info = aIterator.next();
  86. if (info.done)
  87. break;
  88. // If a instanceof Set, aValue === aKey.
  89. var _a = info.value, aKey = _a[0], aValue = _a[1];
  90. // So this works the same way for both Set and Map.
  91. if (!b.has(aKey)) {
  92. return false;
  93. }
  94. // However, we care about deep equality of values only when dealing
  95. // with Map structures.
  96. if (isMap && !check(aValue, b.get(aKey))) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. }
  103. // Otherwise the values are not equal.
  104. return false;
  105. }
  106. function previouslyCompared(a, b) {
  107. // Though cyclic references can make an object graph appear infinite from the
  108. // perspective of a depth-first traversal, the graph still contains a finite
  109. // number of distinct object references. We use the previousComparisons cache
  110. // to avoid comparing the same pair of object references more than once, which
  111. // guarantees termination (even if we end up comparing every object in one
  112. // graph to every object in the other graph, which is extremely unlikely),
  113. // while still allowing weird isomorphic structures (like rings with different
  114. // lengths) a chance to pass the equality test.
  115. var bSet = previousComparisons.get(a);
  116. if (bSet) {
  117. // Return true here because we can be sure false will be returned somewhere
  118. // else if the objects are not equivalent.
  119. if (bSet.has(b))
  120. return true;
  121. }
  122. else {
  123. previousComparisons.set(a, bSet = new Set);
  124. }
  125. bSet.add(b);
  126. return false;
  127. }
  128. exports.default = equal;
  129. exports.equal = equal;
  130. //# sourceMappingURL=equality.js.map