diff --git a/lodash.js b/lodash.js index 2336a2b89..16fe7af71 100644 --- a/lodash.js +++ b/lodash.js @@ -1428,6 +1428,7 @@ clearTimeout = context.clearTimeout, enumerate = Reflect ? Reflect.enumerate : undefined, getPrototypeOf = Object.getPrototypeOf, + getOwnPropertySymbols = Object.getOwnPropertySymbols, iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined, parseFloat = context.parseFloat, pow = Math.pow, @@ -1460,8 +1461,8 @@ var mapCtorString = Map ? funcToString.call(Map) : '', setCtorString = Set ? funcToString.call(Set) : ''; - /** Used to convert symbols to strings. */ - var symbolToString = Symbol ? Symbol.prototype.toString : undefined; + /** Used to convert symbol objects to primitives. */ + var symbolValueOf = Symbol ? Symbol.prototype.valueOf : undefined; /** Used to lookup unminified function names. */ var realNames = {}; @@ -2112,6 +2113,26 @@ /*------------------------------------------------------------------------*/ + /** + * Assigns own symbol properties of `source` to `object`. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function assignSymbols(object, source) { + var index = -1, + symbols = getSymbols(source), + length = symbols.length; + + while (++index < length) { + var symbol = symbols[index]; + object[symbol] = source[symbol]; + } + return object; + } + /** * Removes `key` and its value from the associative array. * @@ -2276,7 +2297,7 @@ if (isArr) { result = initCloneArray(value); if (!isDeep) { - return copyArray(value, result); + result = copyArray(value, result); } } else { var tag = getTag(value), @@ -2288,13 +2309,19 @@ } result = initCloneObject(isFunc ? {} : value); if (!isDeep) { - return baseAssign(result, value); + result = baseAssign(result, value); } - } else { - return cloneableTags[tag] - ? initCloneByTag(value, tag, isDeep) - : (object ? value : {}); } + else if (!cloneableTags[tag]) { + return object ? value : {}; + } + else { + result = initCloneByTag(value, tag, isDeep); + } + } + result = assignSymbols(result, value); + if (!isDeep) { + return result; } // Check for circular references and return its corresponding clone. stack || (stack = new Stack); @@ -2777,10 +2804,7 @@ if (value === other) { return true; } - if (value == null || other == null || ( - !(typeof value == 'symbol' || isObject(value)) && - !(typeof other == 'symbol' || isObjectLike(other)) - )) { + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { return value !== value && other !== other; } return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); @@ -3766,10 +3790,7 @@ * @returns {Object} Returns the cloned symbol. */ function cloneSymbol(symbol) { - var Ctor = symbol.constructor, - result = Ctor(symbolToString.call(symbol).slice(7, -1)); - - return typeof symbol == 'object' ? Object(result) : result; + return typeof symbol == 'object' ? Object(symbolValueOf.call(symbol)) : symbol; } /** @@ -4550,7 +4571,7 @@ equalFunc(convert(object), convert(other), customizer, bitmask | UNORDERED_COMPARE_FLAG); case symbolTag: - return symbolToString.call(object) == symbolToString.call(other); + return symbolValueOf.call(object) == symbolValueOf.call(other); } return false; } @@ -4714,6 +4735,17 @@ return isNative(value) ? value : undefined; } + /** + * Creates an array of the own symbol properties of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = getOwnPropertySymbols || function() { + return []; + }; + /** * Gets the `toStringTag` of `value`. * diff --git a/test/test.js b/test/test.js index d6522bbe4..64154cd69 100644 --- a/test/test.js +++ b/test/test.js @@ -2220,7 +2220,6 @@ 'sets': set, 'strings': 'a', 'string objects': Object('a'), - 'symbols': symbol, 'undefined values': undefined }; @@ -2313,14 +2312,12 @@ QUnit.test('`_.' + methodName + '` should clone ' + key, function(assert) { assert.expect(2); - var useUnstable = /^(?:maps|sets|symbols)$/.test(key), - isEqual = useUnstable ? _.isEqual : lodashStable.isEqual, - isObject = useUnstable ? _.isObject : lodashStable.isObject, + var isEqual = (key == 'maps' || key == 'sets') ? _.isEqual : lodashStable.isEqual, actual = func(object); assert.ok(isEqual(actual, object)); - if (isObject(object)) { + if (lodashStable.isObject(object)) { assert.notStrictEqual(actual, object); } else { assert.strictEqual(actual, object); @@ -2364,10 +2361,12 @@ assert.strictEqual(actual.lastIndex, 3); }); - QUnit.test('`_.' + methodName + '` should clone symbol objects', function(assert) { - assert.expect(3); + QUnit.test('`_.' + methodName + '` should clone symbols', function(assert) { + assert.expect(4); if (Symbol) { + assert.strictEqual(func(symbol), symbol); + var object = Object(symbol), actual = func(object); @@ -2376,7 +2375,7 @@ assert.notStrictEqual(actual, object); } else { - skipTest(assert, 3); + skipTest(assert, 4); } }); @@ -7640,6 +7639,9 @@ QUnit.module('lodash.isEqual'); (function() { + var symbol1 = Symbol ? Symbol('a') : true, + symbol2 = Symbol ? Symbol('b') : false; + QUnit.test('should compare primitives', function(assert) { assert.expect(1); @@ -7650,6 +7652,7 @@ ['a', 'a', true], ['a', Object('a'), true], [Object('a'), Object('a'), true], ['a', 'b', false], ['a', ['a'], false], [true, true, true], [true, Object(true), true], [Object(true), Object(true), true], [true, 1, false], [true, 'a', false], [false, false, true], [false, Object(false), true], [Object(false), Object(false), true], [false, 0, false], [false, '', false], + [symbol1, symbol1, true], [symbol1, Object(symbol1), true], [Object(symbol1), Object(symbol1), true], [symbol1, symbol2, false], [null, null, true], [null, undefined, false], [null, {}, false], [null, '', false], [undefined, undefined, true], [undefined, null, false], [undefined, '', false] ]; @@ -8154,30 +8157,6 @@ } }); - QUnit.test('should compare symbols', function(assert) { - assert.expect(4); - - if (Symbol) { - var symbol1 = Symbol('a'), - symbol2 = Symbol('b'); - - assert.strictEqual(_.isEqual(symbol1, symbol2), false); - - symbol2 = Symbol('a'); - assert.strictEqual(_.isEqual(symbol1, symbol2), true); - - symbol1 = Symbol(); - symbol2 = Symbol(''); - assert.strictEqual(_.isEqual(symbol1, symbol2), true); - - symbol1 = Symbol(null); - assert.strictEqual(_.isEqual(symbol1, symbol2), false); - } - else { - skipTest(assert, 4); - } - }); - QUnit.test('should compare typed arrays', function(assert) { assert.expect(1);