You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
3.4 KiB

4 years ago
  1. var objectKeys = require('object-keys');
  2. var isArguments = require('is-arguments');
  3. var is = require('object-is');
  4. var isRegex = require('is-regex');
  5. var flags = require('regexp.prototype.flags');
  6. var isDate = require('is-date-object');
  7. var getTime = Date.prototype.getTime;
  8. function deepEqual(actual, expected, options) {
  9. var opts = options || {};
  10. // 7.1. All identical values are equivalent, as determined by ===.
  11. if (opts.strict ? is(actual, expected) : actual === expected) {
  12. return true;
  13. }
  14. // 7.3. Other pairs that do not both pass typeof value == 'object', equivalence is determined by ==.
  15. if (!actual || !expected || (typeof actual !== 'object' && typeof expected !== 'object')) {
  16. return opts.strict ? is(actual, expected) : actual == expected;
  17. }
  18. /*
  19. * 7.4. For all other Object pairs, including Array objects, equivalence is
  20. * determined by having the same number of owned properties (as verified
  21. * with Object.prototype.hasOwnProperty.call), the same set of keys
  22. * (although not necessarily the same order), equivalent values for every
  23. * corresponding key, and an identical 'prototype' property. Note: this
  24. * accounts for both named and indexed properties on Arrays.
  25. */
  26. // eslint-disable-next-line no-use-before-define
  27. return objEquiv(actual, expected, opts);
  28. }
  29. function isUndefinedOrNull(value) {
  30. return value === null || value === undefined;
  31. }
  32. function isBuffer(x) {
  33. if (!x || typeof x !== 'object' || typeof x.length !== 'number') {
  34. return false;
  35. }
  36. if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
  37. return false;
  38. }
  39. if (x.length > 0 && typeof x[0] !== 'number') {
  40. return false;
  41. }
  42. return true;
  43. }
  44. function objEquiv(a, b, opts) {
  45. /* eslint max-statements: [2, 50] */
  46. var i, key;
  47. if (typeof a !== typeof b) { return false; }
  48. if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { return false; }
  49. // an identical 'prototype' property.
  50. if (a.prototype !== b.prototype) { return false; }
  51. if (isArguments(a) !== isArguments(b)) { return false; }
  52. var aIsRegex = isRegex(a);
  53. var bIsRegex = isRegex(b);
  54. if (aIsRegex !== bIsRegex) { return false; }
  55. if (aIsRegex || bIsRegex) {
  56. return a.source === b.source && flags(a) === flags(b);
  57. }
  58. if (isDate(a) && isDate(b)) {
  59. return getTime.call(a) === getTime.call(b);
  60. }
  61. var aIsBuffer = isBuffer(a);
  62. var bIsBuffer = isBuffer(b);
  63. if (aIsBuffer !== bIsBuffer) { return false; }
  64. if (aIsBuffer || bIsBuffer) { // && would work too, because both are true or both false here
  65. if (a.length !== b.length) { return false; }
  66. for (i = 0; i < a.length; i++) {
  67. if (a[i] !== b[i]) { return false; }
  68. }
  69. return true;
  70. }
  71. if (typeof a !== typeof b) { return false; }
  72. try {
  73. var ka = objectKeys(a);
  74. var kb = objectKeys(b);
  75. } catch (e) { // happens when one is a string literal and the other isn't
  76. return false;
  77. }
  78. // having the same number of owned properties (keys incorporates hasOwnProperty)
  79. if (ka.length !== kb.length) { return false; }
  80. // the same set of keys (although not necessarily the same order),
  81. ka.sort();
  82. kb.sort();
  83. // ~~~cheap key test
  84. for (i = ka.length - 1; i >= 0; i--) {
  85. if (ka[i] != kb[i]) { return false; }
  86. }
  87. // equivalent values for every corresponding key, and ~~~possibly expensive deep test
  88. for (i = ka.length - 1; i >= 0; i--) {
  89. key = ka[i];
  90. if (!deepEqual(a[key], b[key], opts)) { return false; }
  91. }
  92. return true;
  93. }
  94. module.exports = deepEqual;