diff --git a/lodash.js b/lodash.js index ba62cd54e..90e86e2d7 100644 --- a/lodash.js +++ b/lodash.js @@ -162,16 +162,16 @@ /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; - cloneableClasses[argsClass] = - cloneableClasses[arrayClass] = cloneableClasses[arrayBufferClass] = - cloneableClasses[boolClass] = cloneableClasses[dateClass] = - cloneableClasses[errorClass] = cloneableClasses[float32Class] = + cloneableClasses[argsClass] = cloneableClasses[arrayClass] = + cloneableClasses[arrayBufferClass] = cloneableClasses[boolClass] = + cloneableClasses[dateClass] = cloneableClasses[float32Class] = cloneableClasses[float64Class] = cloneableClasses[int8Class] = cloneableClasses[int16Class] = cloneableClasses[int32Class] = cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = cloneableClasses[uint8Class] = cloneableClasses[uint8ClampedClass] = cloneableClasses[uint16Class] = cloneableClasses[uint32Class] = true; + cloneableClasses[errorClass] = cloneableClasses[funcClass] = cloneableClasses[mapClass] = cloneableClasses[setClass] = cloneableClasses[weakMapClass] = false; @@ -1237,9 +1237,6 @@ case dateClass: return new Ctor(+value); - case errorClass: - return new Ctor(value.message); - case float32Class: case float64Class: case int8Class: case int16Class: case int32Class: 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 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: // treat `NaN` vs. `NaN` as equal return (value != +value) @@ -1804,18 +1797,20 @@ case regexpClass: case stringClass: - // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) - // treat string primitives and object instances as equal + // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) and + // treat strings primitives and string objects as equal return value == String(other); } + var isArr = arrayLikeClasses[valClass], + isErr = valClass == errorClass; + if (!support.argsClass) { valIsArg = isArguments(value); othIsArg = isArguments(other); } - var isArr = arrayLikeClasses[valClass]; if (!isArr) { - // exit for functions and DOM nodes - if (valClass != objectClass || (!support.nodeClass && (isNode(value) || isNode(other)))) { + // exit for things like functions and DOM nodes + if (!(isErr || valClass == objectClass) || (!support.nodeClass && (isNode(value) || isNode(other)))) { return false; } // unwrap any `lodash` wrapped values @@ -1836,6 +1831,10 @@ var valCtor = valIsArg ? Object : value.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 if (valCtor != othCtor && !(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) && @@ -1895,8 +1894,8 @@ } } else { - var valProps = keys(value), - othProps = keys(other); + var valProps = isErr ? ['message', 'name'] : keys(value), + othProps = isErr ? ['message', 'name'] : keys(other); if (valIsArg) { valProps.push('length'); @@ -1910,7 +1909,8 @@ if (result || isWhere) { while (++index < length) { var key = valProps[index]; - result = hasOwnProperty.call(other, key); + result = isErr || hasOwnProperty.call(other, key); + if (result) { valValue = value[key]; othValue = other[key]; diff --git a/test/test.js b/test/test.js index e1803d2a9..6567fdcf9 100644 --- a/test/test.js +++ b/test/test.js @@ -1241,30 +1241,30 @@ Klass.prototype = { 'b': 1 }; var nonCloneable = { - 'a DOM element': body, - 'a function': Klass + 'DOM elements': body, + 'functions': Klass }; var objects = { - 'an `arguments` object': arguments, - 'an array': ['a', ''], - 'an array-like-object': { '0': 'a', '1': '', 'length': 3 }, - 'boolean': false, - 'boolean object': Object(false), - 'a Klass instance': new Klass, - 'an object': { 'a': 0, 'b': 1, 'c': 3 }, - 'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } }, - 'an object from another document': _._object || {}, - 'null': null, - 'a number': 3, - 'a number object': Object(3), - 'a regexp': /a/gim, - 'a string': 'a', - 'a string object': Object('a'), - 'undefined': undefined + '`arguments` objects': arguments, + 'arrays': ['a', ''], + 'array-like-objects': { '0': 'a', '1': '', 'length': 3 }, + 'booleans': false, + 'boolean objects': Object(false), + 'Klass instances': new Klass, + 'objects': { 'a': 0, 'b': 1, 'c': 3 }, + 'objects with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } }, + 'objects from another document': _._object || {}, + 'null values': null, + 'numbers': 3, + 'number objects': Object(3), + 'regexes': /a/gim, + 'strings': 'a', + 'string objects': Object('a'), + 'undefined values': undefined }; - objects['an array'].length = 3; + objects['arrays'].length = 3; test('`_.clone` should shallow clone by default', 2, function() { var expected = [{ 'a': 0 }, { 'b': 1 }], @@ -1313,13 +1313,9 @@ }); _.each(errorTypes, function(type) { - test('`_.' + methodName + '` should clone ' + type + ' objects', 2, function() { - var Ctor = root[type], - error = new Ctor('text'), - actual = func(error); - - ok(_.isEqual(actual, error)); - notStrictEqual(actual, error); + test('`_.' + methodName + '` should not clone ' + type + ' objects', 1, function() { + var error = new root[type]; + strictEqual(func(error), error); }); }); @@ -1426,7 +1422,7 @@ test('`_.' + methodName + '` should return a unwrapped value when chaining', 2, function() { if (!isNpm) { - var object = objects['an object'], + var object = objects['objects'], actual = _(object)[methodName](); deepEqual(actual, object);