From 022035a141f5015342c2b3badecbf74d6432685e Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Tue, 18 Oct 2016 16:44:50 -0400 Subject: [PATCH] fix: compare inherited properties not prototypes - The v1.0.0 refactor created a breaking change by only performing deep equality comparisons between objects' own enumerable properties, and performing a strict equality comparison between objects' prototypes. This fix reverts the behavior so that deep equality comparisons are performed between objects' own and inherited enumerable properties, and no comparison is performed between objects' prototypes. --- index.js | 22 ++++++++++++++++------ test/index.js | 30 ++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 81ee234..3cfdf34 100644 --- a/index.js +++ b/index.js @@ -357,6 +357,20 @@ function getGeneratorEntries(generator) { return accumulator; } +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + /*! * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of * each key. If any value of the given key is not equal, the function will return false (early). @@ -391,12 +405,8 @@ function keysEqual(leftHandOperand, rightHandOperand, keys, options) { */ function objectEqual(leftHandOperand, rightHandOperand, options) { - if (Object.getPrototypeOf(leftHandOperand) !== Object.getPrototypeOf(rightHandOperand)) { - return false; - } - - var leftHandKeys = Object.keys(leftHandOperand); - var rightHandKeys = Object.keys(rightHandOperand); + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { leftHandKeys.sort(); rightHandKeys.sort(); diff --git a/test/index.js b/test/index.js index 825f897..49d289a 100644 --- a/test/index.js +++ b/test/index.js @@ -192,6 +192,15 @@ describe('Generic', function () { assert(eql(new BaseA(1), new BaseA(1)), 'eql(new BaseA(1), new BaseA(1))'); }); + it('returns true given two class instances with deeply equal bases', function () { + function BaseA() {} + function BaseB() {} + BaseA.prototype.foo = { a: 1 }; + BaseB.prototype.foo = { a: 1 }; + assert(eql(new BaseA(), new BaseB()) === true, + 'eql(new , new ) === true'); + }); + it('returns false given two class instances with different properties', function () { function BaseA(prop) { this.prop = prop; @@ -199,10 +208,13 @@ describe('Generic', function () { assert(eql(new BaseA(1), new BaseA(2)) === false, 'eql(new BaseA(1), new BaseA(2)) === false'); }); - it('returns false given two different empty class instances', function () { + it('returns false given two class instances with deeply unequal bases', function () { function BaseA() {} function BaseB() {} - assert(eql(new BaseA(), new BaseB()) === false, 'eql(new BaseA(), new BaseB()) === false'); + BaseA.prototype.foo = { a: 1 }; + BaseB.prototype.foo = { a: 2 }; + assert(eql(new BaseA(), new BaseB()) === false, + 'eql(new , new ) === false'); }); }); @@ -283,6 +295,13 @@ describe('Generic', function () { 'eql({ foo: 1, bar: objectC }, { foo: 1, bar: objectC }) === true'); }); + it('returns true with objects with deeply equal prototypes', function () { + var objectA = Object.create({ foo: { a: 1 } }); + var objectB = Object.create({ foo: { a: 1 } }); + assert(eql(objectA, objectB) === true, + 'eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 1 } })) === true'); + }); + it('returns false with objects containing different literals', function () { assert(eql({ foo: 1, bar: 1 }, { foo: 1, bar: 2 }) === false, 'eql({ foo: 1, bar: 2 }, { foo: 1, bar: 2 }) === false'); @@ -306,6 +325,13 @@ describe('Generic', function () { 'eql({ foo: 1, bar: -> }, { foo: 1, bar: <- }) === false'); }); + it('returns false with objects with deeply unequal prototypes', function () { + var objectA = Object.create({ foo: { a: 1 } }); + var objectB = Object.create({ foo: { a: 2 } }); + assert(eql(objectA, objectB) === false, + 'eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 2 } })) === false'); + }); + }); describe('functions', function () {