mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-01 15:57:48 +00:00
Add support for deep cloning maps and sets.
This commit is contained in:
77
lodash.js
77
lodash.js
@@ -2437,7 +2437,7 @@
|
||||
if (!cloneableTags[tag]) {
|
||||
return object ? value : {};
|
||||
}
|
||||
result = initCloneByTag(value, tag, isDeep);
|
||||
result = initCloneByTag(value, tag, baseClone, isDeep);
|
||||
}
|
||||
}
|
||||
// Check for circular references and return its corresponding clone.
|
||||
@@ -2448,11 +2448,18 @@
|
||||
}
|
||||
stack.set(value, result);
|
||||
|
||||
if (!isArr) {
|
||||
var props = isFull ? getAllKeys(value) : keys(value);
|
||||
}
|
||||
// Recursively populate clone (susceptible to call stack limits).
|
||||
(isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
|
||||
arrayEach(props || value, function(subValue, key) {
|
||||
if (props) {
|
||||
key = subValue;
|
||||
subValue = value[key];
|
||||
}
|
||||
assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
|
||||
});
|
||||
return (isFull && !isArr) ? copySymbols(value, result) : result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2768,6 +2775,24 @@
|
||||
return (index && index == length) ? object : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `getAllKeys` and `getAllKeysIn` which uses
|
||||
* `keysFunc` and `symbolsFunc` to get the enumerable property names and
|
||||
* symbols of `object`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to query.
|
||||
* @param {Function} keysFunc The function to get the keys of `object`.
|
||||
* @param {Function} symbolsFunc The function to get the symbols of `object`.
|
||||
* @returns {Array} Returns the array of property names and symbols.
|
||||
*/
|
||||
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
|
||||
var result = keysFunc(object);
|
||||
return isArray(object)
|
||||
? result
|
||||
: arrayPush(result, symbolsFunc(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.has` without support for deep paths.
|
||||
*
|
||||
@@ -3176,10 +3201,9 @@
|
||||
if (object === source) {
|
||||
return;
|
||||
}
|
||||
var props = (isArray(source) || isTypedArray(source))
|
||||
? undefined
|
||||
: keysIn(source);
|
||||
|
||||
if (!(isArray(source) || isTypedArray(source))) {
|
||||
var props = keysIn(source);
|
||||
}
|
||||
arrayEach(props || source, function(srcValue, key) {
|
||||
if (props) {
|
||||
key = srcValue;
|
||||
@@ -3905,10 +3929,13 @@
|
||||
*
|
||||
* @private
|
||||
* @param {Object} map The map to clone.
|
||||
* @param {Function} cloneFunc The function to clone values.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {Object} Returns the cloned map.
|
||||
*/
|
||||
function cloneMap(map) {
|
||||
return arrayReduce(mapToArray(map), addMapEntry, new map.constructor);
|
||||
function cloneMap(map, isDeep, cloneFunc) {
|
||||
var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
|
||||
return arrayReduce(array, addMapEntry, new map.constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3929,10 +3956,13 @@
|
||||
*
|
||||
* @private
|
||||
* @param {Object} set The set to clone.
|
||||
* @param {Function} cloneFunc The function to clone values.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {Object} Returns the cloned set.
|
||||
*/
|
||||
function cloneSet(set) {
|
||||
return arrayReduce(setToArray(set), addSetEntry, new set.constructor);
|
||||
function cloneSet(set, isDeep, cloneFunc) {
|
||||
var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
|
||||
return arrayReduce(array, addSetEntry, new set.constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4957,7 +4987,18 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of the own and inherited enumerable property names and
|
||||
* Creates an array of own enumerable property names and symbols of `object`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to query.
|
||||
* @returns {Array} Returns the array of property names and symbols.
|
||||
*/
|
||||
function getAllKeys(object) {
|
||||
return baseGetAllKeys(object, keys, getSymbols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of own and inherited enumerable property names and
|
||||
* symbols of `object`.
|
||||
*
|
||||
* @private
|
||||
@@ -4965,10 +5006,7 @@
|
||||
* @returns {Array} Returns the array of property names and symbols.
|
||||
*/
|
||||
function getAllKeysIn(object) {
|
||||
var result = keysIn(object);
|
||||
return isArray(object)
|
||||
? result
|
||||
: arrayPush(result, getSymbolsIn(object));
|
||||
return baseGetAllKeys(object, keysIn, getSymbolsIn);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5258,10 +5296,11 @@
|
||||
* @private
|
||||
* @param {Object} object The object to clone.
|
||||
* @param {string} tag The `toStringTag` of the object to clone.
|
||||
* @param {Function} cloneFunc The function to clone values.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {Object} Returns the initialized clone.
|
||||
*/
|
||||
function initCloneByTag(object, tag, isDeep) {
|
||||
function initCloneByTag(object, tag, cloneFunc, isDeep) {
|
||||
var Ctor = object.constructor;
|
||||
switch (tag) {
|
||||
case arrayBufferTag:
|
||||
@@ -5277,7 +5316,7 @@
|
||||
return cloneTypedArray(object, isDeep);
|
||||
|
||||
case mapTag:
|
||||
return cloneMap(object);
|
||||
return cloneMap(object, isDeep, cloneFunc);
|
||||
|
||||
case numberTag:
|
||||
case stringTag:
|
||||
@@ -5287,7 +5326,7 @@
|
||||
return cloneRegExp(object);
|
||||
|
||||
case setTag:
|
||||
return cloneSet(object);
|
||||
return cloneSet(object, isDeep, cloneFunc);
|
||||
|
||||
case symbolTag:
|
||||
return cloneSymbol(object);
|
||||
|
||||
34
test/test.js
34
test/test.js
@@ -50,6 +50,7 @@
|
||||
create = Object.create,
|
||||
fnToString = funcProto.toString,
|
||||
freeze = Object.freeze,
|
||||
getSymbols = Object.getOwnPropertySymbols,
|
||||
identity = function(value) { return value; },
|
||||
JSON = root.JSON,
|
||||
noop = function() {},
|
||||
@@ -484,7 +485,6 @@
|
||||
};
|
||||
}()));
|
||||
|
||||
var _getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||
setProperty(Object, 'getOwnPropertySymbols', undefined);
|
||||
|
||||
var _propertyIsEnumerable = objectProto.propertyIsEnumerable;
|
||||
@@ -539,8 +539,8 @@
|
||||
setProperty(objectProto, 'propertyIsEnumerable', _propertyIsEnumerable);
|
||||
setProperty(root, 'Buffer', Buffer);
|
||||
|
||||
if (_getOwnPropertySymbols) {
|
||||
Object.getOwnPropertySymbols = _getOwnPropertySymbols;
|
||||
if (getSymbols) {
|
||||
Object.getOwnPropertySymbols = getSymbols;
|
||||
} else {
|
||||
delete Object.getOwnPropertySymbols;
|
||||
}
|
||||
@@ -2741,24 +2741,34 @@
|
||||
});
|
||||
|
||||
QUnit.test('`_.' + methodName + '` should clone symbol properties', function(assert) {
|
||||
assert.expect(2);
|
||||
assert.expect(3);
|
||||
|
||||
function Foo() {
|
||||
this[symbol] = { 'c': 1 };
|
||||
}
|
||||
|
||||
if (Symbol) {
|
||||
var object = {};
|
||||
object[symbol] = {};
|
||||
assert.strictEqual(func(object)[symbol], object[symbol]);
|
||||
var symbol2 = Symbol('b');
|
||||
Foo.prototype[symbol2] = 2;
|
||||
|
||||
var object = { 'a': { 'b': new Foo } };
|
||||
object[symbol] = { 'b': 1 };
|
||||
|
||||
var actual = func(object);
|
||||
|
||||
assert.deepEqual(getSymbols(actual.a.b), [symbol]);
|
||||
|
||||
if (isDeep) {
|
||||
object = { 'a': { 'b': {} } };
|
||||
object.a.b[symbol] = {};
|
||||
assert.strictEqual(func(object).a.b[symbol], object.a.b[symbol]);
|
||||
assert.deepEqual(actual[symbol], object[symbol]);
|
||||
assert.deepEqual(actual.a.b[symbol], object.a.b[symbol]);
|
||||
}
|
||||
else {
|
||||
skipAssert(assert);
|
||||
assert.strictEqual(actual[symbol], object[symbol]);
|
||||
assert.strictEqual(actual.a, object.a);
|
||||
}
|
||||
}
|
||||
else {
|
||||
skipAssert(assert, 2);
|
||||
skipAssert(assert, 3);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user