'use strict' var bufferEquals = require('buffer-equals') function isArguments (object) { return Object.prototype.toString.call(object) === '[object Arguments]' } module.exports = shallower function shallower (a, b) { return shallower_(a, b, [], []) } /** * Based on `only-shallow` by @othiym23. The comments are mostly his, edited * to reflect the usage of strict equality. * * This is a structural equality test, modeled on bits and pieces of loads of * other implementations of this algorithm, most notably the much stricter * `deeper`, from which this comment was copied. * * Everybody who writes one of these functions puts the documentation * inline, which makes it incredibly hard to follow. Here's what this version * of the algorithm does, in order: * * 1. Use strict equality (`===`). * 2. `null` *is* an object – a singleton value object, in fact – so if * either is `null`, return a === b. * 3. Since the only way to make it this far is for `a` or `b` to be an object, if * `a` or `b` is *not* an object, they're clearly not the same. * 4. It's much faster to compare dates by numeric value than by lexical value. * 5. Same goes for Regexps. * 6. The parts of an arguments list most people care about are the arguments * themselves, not the callee, which you shouldn't be looking at anyway. * 7. Objects are more complex: * a. Return `true` if `a` and `b` both have no properties. * b. Ensure that `a` and `b` have the same number of own properties with the * same names (which is what `Object.keys()` returns). * c. Ensure that cyclical references don't blow up the stack. * d. Ensure that all the key names match (faster). * e. Ensure that all of the associated values match, recursively (slower). */ function shallower_ (a, b, ca, cb) { if (typeof a !== 'object' && typeof b !== 'object' && a === b) { return true } else if (a === null || b === null) { return a === b } else if (typeof a !== 'object' || typeof b !== 'object') { return false } else if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) { return bufferEquals(a, b) } else if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime() } else if (a instanceof RegExp && b instanceof RegExp) { return a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.lastIndex === b.lastIndex && a.ignoreCase === b.ignoreCase } else if (isArguments(a) || isArguments(b)) { var slice = Array.prototype.slice return shallower_(slice.call(a), slice.call(b), ca, cb) } else { if (Array.isArray(a) !== Array.isArray(b)) return false var ka = Object.keys(a) var kb = Object.keys(b) // don't bother with stack acrobatics if there's nothing there if (ka.length === 0 && kb.length === 0) return true if (ka.length !== kb.length) return false var cal = ca.length while (cal--) if (ca[cal] === a) return cb[cal] === b ca.push(a); cb.push(b) ka.sort(); kb.sort() for (var k = ka.length - 1; k >= 0; k--) if (ka[k] !== kb[k]) return false var key for (var l = ka.length - 1; l >= 0; l--) { key = ka[l] if (!shallower_(a[key], b[key], ca, cb)) return false } ca.pop(); cb.pop() return true } }