Skip to content

Commit

Permalink
feat: try native structuredClone in cloneDeep
Browse files Browse the repository at this point in the history
resolves lodash#5833

I tried to add tests but they don't seem to be running at the moment,
this can be adjusted as necessary

Any feedback on the proposed approach and any adjustments or additions
is greatly appreciated. Additionally, insights on enhancing the
implementation are welcome as well.
  • Loading branch information
lewxdev committed Apr 29, 2024
1 parent a67a085 commit 36d7d4f
Show file tree
Hide file tree
Showing 3 changed files with 673 additions and 653 deletions.
27 changes: 24 additions & 3 deletions src/cloneDeep.ts
Expand Up @@ -6,11 +6,18 @@ const CLONE_SYMBOLS_FLAG = 4;

/**
* This method is like `clone` except that it recursively clones `value`.
* Object inheritance is preserved.
* Object inheritance is preserved. The method will attempt to use the native
* [`structuredClone`](https://developer.mozilla.org/docs/Web/API/structuredClone)
* function, if `value` [is supported](https://developer.mozilla.org/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types)
* (and the native implementation is available). Otherwise it will fallback to a
* custom implementation.
*
* @since 1.0.0
* @category Lang
* @param {*} value The value to recursively clone.
* @param {boolean} [skipNativeCheck]
* Skip the native check and use the custom implementation. This is useful when
* `value` is known to be incompatible `structuredClone`.
* @returns {*} Returns the deep cloned value.
* @see clone
* @example
Expand All @@ -20,9 +27,23 @@ const CLONE_SYMBOLS_FLAG = 4;
* const deep = cloneDeep(objects)
* console.log(deep[0] === objects[0])
* // => false
*
* // The `skipNativeCheck` flag
* const unsupportedNativeObject = { fn: () => 'a' };
*
* const deep = cloneDeep(unsupportedNativeObject, true);
* console.log(deep === unsupportedNativeObject);
* // => false
*/
function cloneDeep(value) {
return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
function cloneDeep(value, skipNativeCheck) {
try {
if (!skipNativeCheck && structuredClone) {
return structuredClone(value);
}
throw new Error('Unsupported structured clone');
} catch {
return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
}
}

export default cloneDeep;
8 changes: 8 additions & 0 deletions test/clone-methods.spec.js
Expand Up @@ -117,6 +117,14 @@ xdescribe('clone methods', function () {
assert.notStrictEqual(actual, cyclical[`v${LARGE_ARRAY_SIZE - 1}`]);
});

it('`_.cloneDeep` should accept the `skipNativeCheck` flag', () => {
const object = { primitive: 'a', fn: () => 'b' };
const actual = cloneDeep(object, true);

expect(actual).not.toEqual(object);
expect(actual.primitive).not.toEqual(object.primitive);
});

it('`_.cloneDeepWith` should provide `stack` to `customizer`', () => {
let actual;

Expand Down

0 comments on commit 36d7d4f

Please sign in to comment.