mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-10 02:47: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') {
|
if (typeof result != 'undefined') {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var isArr = isArray(value);
|
if (!isObject(value)) {
|
||||||
result = value;
|
return 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 (!isDeep || result === value) {
|
var isArr = isArray(value);
|
||||||
return result;
|
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.
|
// Check for circular references and return corresponding clone.
|
||||||
stackA || (stackA = []);
|
stackA || (stackA = []);
|
||||||
@@ -3589,22 +3603,16 @@
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Array} array The array to clone.
|
* @param {Array} array The array to clone.
|
||||||
* @param {boolean} [isDeep] Specify a deep clone.
|
* @returns {Array} Returns the initialized clone.
|
||||||
* @returns {Array} Returns the initialized array clone.
|
|
||||||
*/
|
*/
|
||||||
function initArrayClone(array, isDeep) {
|
function initCloneArray(array) {
|
||||||
var length = array.length,
|
var length = array.length,
|
||||||
result = new array.constructor(length);
|
result = new array.constructor(length);
|
||||||
|
|
||||||
if (length) {
|
// Add array properties assigned by `RegExp#exec`.
|
||||||
if (!isDeep) {
|
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
|
||||||
arrayCopy(array, result);
|
result.index = array.index;
|
||||||
}
|
result.input = array.input;
|
||||||
// Add array properties assigned by `RegExp#exec`.
|
|
||||||
if (typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
|
|
||||||
result.index = array.index;
|
|
||||||
result.input = array.input;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3614,30 +3622,37 @@
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} object The object to clone.
|
* @param {Object} object The object to clone.
|
||||||
* @param {boolean} [isDeep] Specify a deep clone.
|
* @returns {Object} Returns the initialized clone.
|
||||||
* @returns {null|Object} Returns the initialized object clone if an object
|
|
||||||
* is cloneable, else `null`.
|
|
||||||
*/
|
*/
|
||||||
function initObjectClone(object, isDeep) {
|
function initCloneObject(object) {
|
||||||
if (!isCloneable(object)) {
|
var Ctor = object.constructor;
|
||||||
return null;
|
if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
|
||||||
}
|
|
||||||
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)) {
|
|
||||||
Ctor = Object;
|
Ctor = Object;
|
||||||
}
|
}
|
||||||
if (isArgs || isObj) {
|
return new Ctor;
|
||||||
var result = isDeep ? new Ctor : baseAssign(new Ctor, object);
|
}
|
||||||
if (isArgs) {
|
|
||||||
result.length = object.length;
|
/**
|
||||||
}
|
* Initializes an object clone based on its `toStringTag`.
|
||||||
return result;
|
*
|
||||||
}
|
* **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) {
|
switch (tag) {
|
||||||
|
case argsTag:
|
||||||
|
var result = new Ctor;
|
||||||
|
result.length = object.length;
|
||||||
|
return arrayCopy(object, result);
|
||||||
|
|
||||||
case arrayBufferTag:
|
case arrayBufferTag:
|
||||||
return bufferClone(object);
|
return bufferClone(object);
|
||||||
|
|
||||||
|
|||||||
21
test/test.js
21
test/test.js
@@ -1670,6 +1670,7 @@
|
|||||||
(function() {
|
(function() {
|
||||||
function Klass() { this.a = 1; }
|
function Klass() { this.a = 1; }
|
||||||
Klass.prototype = { 'b': 1 };
|
Klass.prototype = { 'b': 1 };
|
||||||
|
Klass.foo = function() {};
|
||||||
|
|
||||||
var objects = {
|
var objects = {
|
||||||
'`arguments` objects': arguments,
|
'`arguments` objects': arguments,
|
||||||
@@ -1692,13 +1693,13 @@
|
|||||||
|
|
||||||
objects['arrays'].length = 3;
|
objects['arrays'].length = 3;
|
||||||
|
|
||||||
var nonCloneable = {
|
var uncloneable = {
|
||||||
'DOM elements': body,
|
'DOM elements': body,
|
||||||
'functions': Klass
|
'functions': Klass
|
||||||
};
|
};
|
||||||
|
|
||||||
_.each(errors, function(error) {
|
_.each(errors, function(error) {
|
||||||
nonCloneable[error.name + 's'] = error;
|
uncloneable[error.name + 's'] = error;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`_.clone` should perform a shallow clone', 2, function() {
|
test('`_.clone` should perform a shallow clone', 2, function() {
|
||||||
@@ -1747,18 +1748,16 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
_.forOwn(nonCloneable, function(value, key) {
|
_.forOwn(uncloneable, function(value, key) {
|
||||||
test('`_.' + methodName + '` should not clone ' + key, 2, function() {
|
test('`_.' + methodName + '` should not clone ' + key, 3, function() {
|
||||||
var object = { 'a': value, 'b': { 'c': value } },
|
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);
|
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() {
|
test('`_.' + methodName + '` should work with a `customizer` callback and ' + key, 4, function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user