mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-02 08:07:50 +00:00
Add initCloneByTag and adjust how unclonables are handled.
This commit is contained in:
107
lodash.compat.js
107
lodash.compat.js
@@ -1815,21 +1815,35 @@
|
||||
if (typeof result != 'undefined') {
|
||||
return result;
|
||||
}
|
||||
var isArr = isArray(value);
|
||||
result = value;
|
||||
if (isArr) {
|
||||
result = initArrayClone(value, isDeep);
|
||||
} else if (isObject(value)) {
|
||||
result = initObjectClone(value, isDeep);
|
||||
if (result === null) {
|
||||
isDeep = false;
|
||||
result = {};
|
||||
} else if (isDeep) {
|
||||
isDeep = objToString.call(result) == objectTag;
|
||||
}
|
||||
if (!isObject(value)) {
|
||||
return value;
|
||||
}
|
||||
if (!isDeep || result === value) {
|
||||
return result;
|
||||
var isArr = isArray(value);
|
||||
if (isArr) {
|
||||
result = initCloneArray(value);
|
||||
if (!isDeep) {
|
||||
return arrayCopy(value, result);
|
||||
}
|
||||
} else {
|
||||
var tag = objToString.call(value),
|
||||
isFunc = tag == funcTag;
|
||||
|
||||
if (!lodash.support.argsTag && isArguments(value)) {
|
||||
tag = argsTag;
|
||||
}
|
||||
if (tag == objectTag || (isFunc && !object)) {
|
||||
if (isHostObject(value)) {
|
||||
return object ? value : {};
|
||||
}
|
||||
result = initCloneObject(isFunc ? {} : value);
|
||||
if (!isDeep) {
|
||||
return baseAssign(result, value);
|
||||
}
|
||||
} else {
|
||||
return cloneableTags[tag]
|
||||
? initCloneByTag(value, tag, isDeep)
|
||||
: (object ? value : {});
|
||||
}
|
||||
}
|
||||
// Check for circular references and return corresponding clone.
|
||||
stackA || (stackA = []);
|
||||
@@ -3589,22 +3603,16 @@
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to clone.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {Array} Returns the initialized array clone.
|
||||
* @returns {Array} Returns the initialized clone.
|
||||
*/
|
||||
function initArrayClone(array, isDeep) {
|
||||
function initCloneArray(array) {
|
||||
var length = array.length,
|
||||
result = new array.constructor(length);
|
||||
|
||||
if (length) {
|
||||
if (!isDeep) {
|
||||
arrayCopy(array, result);
|
||||
}
|
||||
// Add array properties assigned by `RegExp#exec`.
|
||||
if (typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
|
||||
result.index = array.index;
|
||||
result.input = array.input;
|
||||
}
|
||||
// Add array properties assigned by `RegExp#exec`.
|
||||
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
|
||||
result.index = array.index;
|
||||
result.input = array.input;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3614,30 +3622,37 @@
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to clone.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {null|Object} Returns the initialized object clone if an object
|
||||
* is cloneable, else `null`.
|
||||
* @returns {Object} Returns the initialized clone.
|
||||
*/
|
||||
function initObjectClone(object, isDeep) {
|
||||
if (!isCloneable(object)) {
|
||||
return null;
|
||||
}
|
||||
var Ctor = object.constructor,
|
||||
tag = objToString.call(object),
|
||||
isArgs = tag == argsTag || (!lodash.support.argsTag && isArguments(object)),
|
||||
isObj = tag == objectTag;
|
||||
|
||||
if (isObj && !(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
|
||||
function initCloneObject(object) {
|
||||
var Ctor = object.constructor;
|
||||
if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
|
||||
Ctor = Object;
|
||||
}
|
||||
if (isArgs || isObj) {
|
||||
var result = isDeep ? new Ctor : baseAssign(new Ctor, object);
|
||||
if (isArgs) {
|
||||
result.length = object.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return new Ctor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an object clone based on its `toStringTag`.
|
||||
*
|
||||
* **Note:** This function only supports cloning values with `toStringTag`
|
||||
* values of `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to clone.
|
||||
* @param {string} tag The `toStringTag` of the object to clone.
|
||||
* @param {boolean} [isDeep] Specify a deep clone.
|
||||
* @returns {Object} Returns the initialized clone.
|
||||
*/
|
||||
function initCloneByTag(object, tag, isDeep) {
|
||||
var Ctor = object.constructor;
|
||||
switch (tag) {
|
||||
case argsTag:
|
||||
var result = new Ctor;
|
||||
result.length = object.length;
|
||||
return arrayCopy(object, result);
|
||||
|
||||
case arrayBufferTag:
|
||||
return bufferClone(object);
|
||||
|
||||
|
||||
21
test/test.js
21
test/test.js
@@ -1670,6 +1670,7 @@
|
||||
(function() {
|
||||
function Klass() { this.a = 1; }
|
||||
Klass.prototype = { 'b': 1 };
|
||||
Klass.foo = function() {};
|
||||
|
||||
var objects = {
|
||||
'`arguments` objects': arguments,
|
||||
@@ -1692,13 +1693,13 @@
|
||||
|
||||
objects['arrays'].length = 3;
|
||||
|
||||
var nonCloneable = {
|
||||
var uncloneable = {
|
||||
'DOM elements': body,
|
||||
'functions': Klass
|
||||
};
|
||||
|
||||
_.each(errors, function(error) {
|
||||
nonCloneable[error.name + 's'] = error;
|
||||
uncloneable[error.name + 's'] = error;
|
||||
});
|
||||
|
||||
test('`_.clone` should perform a shallow clone', 2, function() {
|
||||
@@ -1747,18 +1748,16 @@
|
||||
});
|
||||
});
|
||||
|
||||
_.forOwn(nonCloneable, function(value, key) {
|
||||
test('`_.' + methodName + '` should not clone ' + key, 2, function() {
|
||||
_.forOwn(uncloneable, function(value, key) {
|
||||
test('`_.' + methodName + '` should not clone ' + key, 3, function() {
|
||||
var object = { 'a': value, 'b': { 'c': value } },
|
||||
expected = value && {};
|
||||
actual = func(object);
|
||||
|
||||
notStrictEqual(actual, object);
|
||||
deepEqual(actual, object);
|
||||
|
||||
var expected = typeof value == 'function' ? { 'foo': Klass.foo } : {};
|
||||
deepEqual(func(value), expected);
|
||||
|
||||
expected = isDeep
|
||||
? { 'a': expected, 'b': { 'c': expected } }
|
||||
: { 'a': value, 'b': { 'c': value } }
|
||||
|
||||
deepEqual(func(object), expected);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should work with a `customizer` callback and ' + key, 4, function() {
|
||||
|
||||
Reference in New Issue
Block a user