| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- /*!
- * deep-eql
- * Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
- * MIT Licensed
- */
- /*!
- * Module dependencies
- */
- var type = require('type-detect');
- /*!
- * Buffer.isBuffer browser shim
- */
- var Buffer;
- try { Buffer = require('buffer').Buffer; }
- catch(ex) {
- Buffer = {};
- Buffer.isBuffer = function() { return false; }
- }
- /*!
- * Primary Export
- */
- module.exports = deepEqual;
- /**
- * Assert super-strict (egal) equality between
- * two objects of any type.
- *
- * @param {Mixed} a
- * @param {Mixed} b
- * @param {Array} memoised (optional)
- * @return {Boolean} equal match
- */
- function deepEqual(a, b, m) {
- if (sameValue(a, b)) {
- return true;
- } else if ('date' === type(a)) {
- return dateEqual(a, b);
- } else if ('regexp' === type(a)) {
- return regexpEqual(a, b);
- } else if (Buffer.isBuffer(a)) {
- return bufferEqual(a, b);
- } else if ('arguments' === type(a)) {
- return argumentsEqual(a, b, m);
- } else if (!typeEqual(a, b)) {
- return false;
- } else if (('object' !== type(a) && 'object' !== type(b))
- && ('array' !== type(a) && 'array' !== type(b))) {
- return sameValue(a, b);
- } else {
- return objectEqual(a, b, m);
- }
- }
- /*!
- * Strict (egal) equality test. Ensures that NaN always
- * equals NaN and `-0` does not equal `+0`.
- *
- * @param {Mixed} a
- * @param {Mixed} b
- * @return {Boolean} equal match
- */
- function sameValue(a, b) {
- if (a === b) return a !== 0 || 1 / a === 1 / b;
- return a !== a && b !== b;
- }
- /*!
- * Compare the types of two given objects and
- * return if they are equal. Note that an Array
- * has a type of `array` (not `object`) and arguments
- * have a type of `arguments` (not `array`/`object`).
- *
- * @param {Mixed} a
- * @param {Mixed} b
- * @return {Boolean} result
- */
- function typeEqual(a, b) {
- return type(a) === type(b);
- }
- /*!
- * Compare two Date objects by asserting that
- * the time values are equal using `saveValue`.
- *
- * @param {Date} a
- * @param {Date} b
- * @return {Boolean} result
- */
- function dateEqual(a, b) {
- if ('date' !== type(b)) return false;
- return sameValue(a.getTime(), b.getTime());
- }
- /*!
- * Compare two regular expressions by converting them
- * to string and checking for `sameValue`.
- *
- * @param {RegExp} a
- * @param {RegExp} b
- * @return {Boolean} result
- */
- function regexpEqual(a, b) {
- if ('regexp' !== type(b)) return false;
- return sameValue(a.toString(), b.toString());
- }
- /*!
- * Assert deep equality of two `arguments` objects.
- * Unfortunately, these must be sliced to arrays
- * prior to test to ensure no bad behavior.
- *
- * @param {Arguments} a
- * @param {Arguments} b
- * @param {Array} memoize (optional)
- * @return {Boolean} result
- */
- function argumentsEqual(a, b, m) {
- if ('arguments' !== type(b)) return false;
- a = [].slice.call(a);
- b = [].slice.call(b);
- return deepEqual(a, b, m);
- }
- /*!
- * Get enumerable properties of a given object.
- *
- * @param {Object} a
- * @return {Array} property names
- */
- function enumerable(a) {
- var res = [];
- for (var key in a) res.push(key);
- return res;
- }
- /*!
- * Simple equality for flat iterable objects
- * such as Arrays or Node.js buffers.
- *
- * @param {Iterable} a
- * @param {Iterable} b
- * @return {Boolean} result
- */
- function iterableEqual(a, b) {
- if (a.length !== b.length) return false;
- var i = 0;
- var match = true;
- for (; i < a.length; i++) {
- if (a[i] !== b[i]) {
- match = false;
- break;
- }
- }
- return match;
- }
- /*!
- * Extension to `iterableEqual` specifically
- * for Node.js Buffers.
- *
- * @param {Buffer} a
- * @param {Mixed} b
- * @return {Boolean} result
- */
- function bufferEqual(a, b) {
- if (!Buffer.isBuffer(b)) return false;
- return iterableEqual(a, b);
- }
- /*!
- * Block for `objectEqual` ensuring non-existing
- * values don't get in.
- *
- * @param {Mixed} object
- * @return {Boolean} result
- */
- function isValue(a) {
- return a !== null && a !== undefined;
- }
- /*!
- * Recursively check the equality of two objects.
- * Once basic sameness has been established it will
- * defer to `deepEqual` for each enumerable key
- * in the object.
- *
- * @param {Mixed} a
- * @param {Mixed} b
- * @return {Boolean} result
- */
- function objectEqual(a, b, m) {
- if (!isValue(a) || !isValue(b)) {
- return false;
- }
- if (a.prototype !== b.prototype) {
- return false;
- }
- var i;
- if (m) {
- for (i = 0; i < m.length; i++) {
- if ((m[i][0] === a && m[i][1] === b)
- || (m[i][0] === b && m[i][1] === a)) {
- return true;
- }
- }
- } else {
- m = [];
- }
- try {
- var ka = enumerable(a);
- var kb = enumerable(b);
- } catch (ex) {
- return false;
- }
- ka.sort();
- kb.sort();
- if (!iterableEqual(ka, kb)) {
- return false;
- }
- m.push([ a, b ]);
- var key;
- for (i = ka.length - 1; i >= 0; i--) {
- key = ka[i];
- if (!deepEqual(a[key], b[key], m)) {
- return false;
- }
- }
- return true;
- }
|