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