Skip to content

Commit 022035a

Browse files
committed
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.
1 parent 617b392 commit 022035a

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

index.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,20 @@ function getGeneratorEntries(generator) {
357357
return accumulator;
358358
}
359359

360+
/*!
361+
* Gets all own and inherited enumerable keys from a target.
362+
*
363+
* @param {Object} target
364+
* @returns {Array} an array of own and inherited enumerable keys from the target.
365+
*/
366+
function getEnumerableKeys(target) {
367+
var keys = [];
368+
for (var key in target) {
369+
keys.push(key);
370+
}
371+
return keys;
372+
}
373+
360374
/*!
361375
* Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of
362376
* 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) {
391405
*/
392406

393407
function objectEqual(leftHandOperand, rightHandOperand, options) {
394-
if (Object.getPrototypeOf(leftHandOperand) !== Object.getPrototypeOf(rightHandOperand)) {
395-
return false;
396-
}
397-
398-
var leftHandKeys = Object.keys(leftHandOperand);
399-
var rightHandKeys = Object.keys(rightHandOperand);
408+
var leftHandKeys = getEnumerableKeys(leftHandOperand);
409+
var rightHandKeys = getEnumerableKeys(rightHandOperand);
400410
if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) {
401411
leftHandKeys.sort();
402412
rightHandKeys.sort();

test/index.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,17 +192,29 @@ describe('Generic', function () {
192192
assert(eql(new BaseA(1), new BaseA(1)), 'eql(new BaseA(1), new BaseA(1))');
193193
});
194194

195+
it('returns true given two class instances with deeply equal bases', function () {
196+
function BaseA() {}
197+
function BaseB() {}
198+
BaseA.prototype.foo = { a: 1 };
199+
BaseB.prototype.foo = { a: 1 };
200+
assert(eql(new BaseA(), new BaseB()) === true,
201+
'eql(new <base with .prototype.foo = { a: 1 }>, new <base with .prototype.foo = { a: 1 }>) === true');
202+
});
203+
195204
it('returns false given two class instances with different properties', function () {
196205
function BaseA(prop) {
197206
this.prop = prop;
198207
}
199208
assert(eql(new BaseA(1), new BaseA(2)) === false, 'eql(new BaseA(1), new BaseA(2)) === false');
200209
});
201210

202-
it('returns false given two different empty class instances', function () {
211+
it('returns false given two class instances with deeply unequal bases', function () {
203212
function BaseA() {}
204213
function BaseB() {}
205-
assert(eql(new BaseA(), new BaseB()) === false, 'eql(new BaseA(), new BaseB()) === false');
214+
BaseA.prototype.foo = { a: 1 };
215+
BaseB.prototype.foo = { a: 2 };
216+
assert(eql(new BaseA(), new BaseB()) === false,
217+
'eql(new <base with .prototype.foo = { a: 1 }>, new <base with .prototype.foo = { a: 2 }>) === false');
206218
});
207219

208220
});
@@ -283,6 +295,13 @@ describe('Generic', function () {
283295
'eql({ foo: 1, bar: objectC }, { foo: 1, bar: objectC }) === true');
284296
});
285297

298+
it('returns true with objects with deeply equal prototypes', function () {
299+
var objectA = Object.create({ foo: { a: 1 } });
300+
var objectB = Object.create({ foo: { a: 1 } });
301+
assert(eql(objectA, objectB) === true,
302+
'eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 1 } })) === true');
303+
});
304+
286305
it('returns false with objects containing different literals', function () {
287306
assert(eql({ foo: 1, bar: 1 }, { foo: 1, bar: 2 }) === false,
288307
'eql({ foo: 1, bar: 2 }, { foo: 1, bar: 2 }) === false');
@@ -306,6 +325,13 @@ describe('Generic', function () {
306325
'eql({ foo: 1, bar: -> }, { foo: 1, bar: <- }) === false');
307326
});
308327

328+
it('returns false with objects with deeply unequal prototypes', function () {
329+
var objectA = Object.create({ foo: { a: 1 } });
330+
var objectB = Object.create({ foo: { a: 2 } });
331+
assert(eql(objectA, objectB) === false,
332+
'eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 2 } })) === false');
333+
});
334+
309335
});
310336

311337
describe('functions', function () {

0 commit comments

Comments
 (0)