Remove support for cloning arrays and make _.isEqual more robust when comparing errors.

This commit is contained in:
John-David Dalton
2014-06-12 08:38:35 -07:00
parent d06a477371
commit b6bc844b4d
2 changed files with 42 additions and 46 deletions

View File

@@ -162,16 +162,16 @@
/** Used to identify object classifications that `_.clone` supports */ /** Used to identify object classifications that `_.clone` supports */
var cloneableClasses = {}; var cloneableClasses = {};
cloneableClasses[argsClass] = cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
cloneableClasses[arrayClass] = cloneableClasses[arrayBufferClass] = cloneableClasses[arrayBufferClass] = cloneableClasses[boolClass] =
cloneableClasses[boolClass] = cloneableClasses[dateClass] = cloneableClasses[dateClass] = cloneableClasses[float32Class] =
cloneableClasses[errorClass] = cloneableClasses[float32Class] =
cloneableClasses[float64Class] = cloneableClasses[int8Class] = cloneableClasses[float64Class] = cloneableClasses[int8Class] =
cloneableClasses[int16Class] = cloneableClasses[int32Class] = cloneableClasses[int16Class] = cloneableClasses[int32Class] =
cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[numberClass] = cloneableClasses[objectClass] =
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] =
cloneableClasses[uint8Class] = cloneableClasses[uint8ClampedClass] = cloneableClasses[uint8Class] = cloneableClasses[uint8ClampedClass] =
cloneableClasses[uint16Class] = cloneableClasses[uint32Class] = true; cloneableClasses[uint16Class] = cloneableClasses[uint32Class] = true;
cloneableClasses[errorClass] =
cloneableClasses[funcClass] = cloneableClasses[mapClass] = cloneableClasses[funcClass] = cloneableClasses[mapClass] =
cloneableClasses[setClass] = cloneableClasses[weakMapClass] = false; cloneableClasses[setClass] = cloneableClasses[weakMapClass] = false;
@@ -1237,9 +1237,6 @@
case dateClass: case dateClass:
return new Ctor(+value); return new Ctor(+value);
case errorClass:
return new Ctor(value.message);
case float32Class: case float64Class: case float32Class: case float64Class:
case int8Class: case int16Class: case int32Class: case int8Class: case int16Class: case int32Class:
case uint8Class: case uint8ClampedClass: case uint16Class: case uint32Class: case uint8Class: case uint8ClampedClass: case uint16Class: case uint32Class:
@@ -1791,10 +1788,6 @@
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
return +value == +other; return +value == +other;
case errorClass:
// check properties instead of coercing to strings to support IE < 8
return value.name === other.name && value.message === other.message;
case numberClass: case numberClass:
// treat `NaN` vs. `NaN` as equal // treat `NaN` vs. `NaN` as equal
return (value != +value) return (value != +value)
@@ -1804,18 +1797,20 @@
case regexpClass: case regexpClass:
case stringClass: case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4) // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) and
// treat string primitives and object instances as equal // treat strings primitives and string objects as equal
return value == String(other); return value == String(other);
} }
var isArr = arrayLikeClasses[valClass],
isErr = valClass == errorClass;
if (!support.argsClass) { if (!support.argsClass) {
valIsArg = isArguments(value); valIsArg = isArguments(value);
othIsArg = isArguments(other); othIsArg = isArguments(other);
} }
var isArr = arrayLikeClasses[valClass];
if (!isArr) { if (!isArr) {
// exit for functions and DOM nodes // exit for things like functions and DOM nodes
if (valClass != objectClass || (!support.nodeClass && (isNode(value) || isNode(other)))) { if (!(isErr || valClass == objectClass) || (!support.nodeClass && (isNode(value) || isNode(other)))) {
return false; return false;
} }
// unwrap any `lodash` wrapped values // unwrap any `lodash` wrapped values
@@ -1836,6 +1831,10 @@
var valCtor = valIsArg ? Object : value.constructor, var valCtor = valIsArg ? Object : value.constructor,
othCtor = othIsArg ? Object : other.constructor; othCtor = othIsArg ? Object : other.constructor;
// error objects of different types are not equal
if (isErr && valCtor.prototype.name != othCtor.prototype.name) {
return false;
}
// non `Object` object instances with different constructors are not equal // non `Object` object instances with different constructors are not equal
if (valCtor != othCtor && if (valCtor != othCtor &&
!(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) && !(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) &&
@@ -1895,8 +1894,8 @@
} }
} }
else { else {
var valProps = keys(value), var valProps = isErr ? ['message', 'name'] : keys(value),
othProps = keys(other); othProps = isErr ? ['message', 'name'] : keys(other);
if (valIsArg) { if (valIsArg) {
valProps.push('length'); valProps.push('length');
@@ -1910,7 +1909,8 @@
if (result || isWhere) { if (result || isWhere) {
while (++index < length) { while (++index < length) {
var key = valProps[index]; var key = valProps[index];
result = hasOwnProperty.call(other, key); result = isErr || hasOwnProperty.call(other, key);
if (result) { if (result) {
valValue = value[key]; valValue = value[key];
othValue = other[key]; othValue = other[key];

View File

@@ -1241,30 +1241,30 @@
Klass.prototype = { 'b': 1 }; Klass.prototype = { 'b': 1 };
var nonCloneable = { var nonCloneable = {
'a DOM element': body, 'DOM elements': body,
'a function': Klass 'functions': Klass
}; };
var objects = { var objects = {
'an `arguments` object': arguments, '`arguments` objects': arguments,
'an array': ['a', ''], 'arrays': ['a', ''],
'an array-like-object': { '0': 'a', '1': '', 'length': 3 }, 'array-like-objects': { '0': 'a', '1': '', 'length': 3 },
'boolean': false, 'booleans': false,
'boolean object': Object(false), 'boolean objects': Object(false),
'a Klass instance': new Klass, 'Klass instances': new Klass,
'an object': { 'a': 0, 'b': 1, 'c': 3 }, 'objects': { 'a': 0, 'b': 1, 'c': 3 },
'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } }, 'objects with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
'an object from another document': _._object || {}, 'objects from another document': _._object || {},
'null': null, 'null values': null,
'a number': 3, 'numbers': 3,
'a number object': Object(3), 'number objects': Object(3),
'a regexp': /a/gim, 'regexes': /a/gim,
'a string': 'a', 'strings': 'a',
'a string object': Object('a'), 'string objects': Object('a'),
'undefined': undefined 'undefined values': undefined
}; };
objects['an array'].length = 3; objects['arrays'].length = 3;
test('`_.clone` should shallow clone by default', 2, function() { test('`_.clone` should shallow clone by default', 2, function() {
var expected = [{ 'a': 0 }, { 'b': 1 }], var expected = [{ 'a': 0 }, { 'b': 1 }],
@@ -1313,13 +1313,9 @@
}); });
_.each(errorTypes, function(type) { _.each(errorTypes, function(type) {
test('`_.' + methodName + '` should clone ' + type + ' objects', 2, function() { test('`_.' + methodName + '` should not clone ' + type + ' objects', 1, function() {
var Ctor = root[type], var error = new root[type];
error = new Ctor('text'), strictEqual(func(error), error);
actual = func(error);
ok(_.isEqual(actual, error));
notStrictEqual(actual, error);
}); });
}); });
@@ -1426,7 +1422,7 @@
test('`_.' + methodName + '` should return a unwrapped value when chaining', 2, function() { test('`_.' + methodName + '` should return a unwrapped value when chaining', 2, function() {
if (!isNpm) { if (!isNpm) {
var object = objects['an object'], var object = objects['objects'],
actual = _(object)[methodName](); actual = _(object)[methodName]();
deepEqual(actual, object); deepEqual(actual, object);