Prevent Copying TypedArrays in Lodash cloneDeepWith

Lodash's standard _.cloneDeep utility recursively clones every nested structure, including typed arrays and their underlying memory buffers. When working with massive binary datasets, WebGL buffers, or audio data, this behavior leads to significant memory spikes and performance bottlenecks. By utilizing _.cloneDeepWith and providing a custom interceptor function, you can identify TypedArray instances and return their original references directly, bypassing unnecessary memory allocation while still deeply cloning the rest of the object graph.

The Problem with Default Deep Cloning

By default, _.cloneDeep creates a new instance of any TypedArray (such as Float32Array, Uint8Array, or BigInt64Array) and allocates a brand-new ArrayBuffer with duplicated binary data. When handling arrays containing millions of elements, this doubling of memory allocation can quickly degrade performance or trigger out-of-memory crashes.

Implementing the Customizer Function

The _.cloneDeepWith method accepts a customizer callback with the signature customizer(value, key, object, stack).

To prevent cloning large typed arrays:

  1. Check if the current value is a TypedArray.
  2. Optionally check if its byteLength exceeds your threshold for what constitutes "huge".
  3. Return the value directly to reuse the reference.
  4. Return undefined for all other values so Lodash falls back to its standard deep-cloning logic.

Custom Cloning Implementation

The most reliable way to check for a TypedArray in modern JavaScript is using ArrayBuffer.isView(), excluding DataView if you wish to clone standard data views.

const _ = require('lodash');

function cloneWithoutHugeTypedArrays(value, byteThreshold = 1024 * 1024) {
  return _.cloneDeepWith(value, (val) => {
    // Check if the value is a TypedArray (ArrayBuffer view, excluding DataView)
    if (ArrayBuffer.isView(val) && !(val instanceof DataView)) {
      // Retain the reference if it exceeds the size threshold
      if (val.byteLength >= byteThreshold) {
        return val;
      }
    }

    // Returning undefined instructs Lodash to use default deep cloning
    return undefined;
  });
}

Usage Example

// Complex state containing normal objects and a large TypedArray
const state = {
  id: "dataset-01",
  metadata: {
    tags: ["production", "sensor-data"],
    author: { name: "Engineer" }
  },
  // 10 million 32-bit floats (~40 MB)
  hugeBuffer: new Float32Array(10_000_000)
};

// Perform the clone
const clonedState = cloneWithoutHugeTypedArrays(state);

// Normal properties are deeply cloned (mutating one does not affect the other)
clonedState.metadata.author.name = "Architect";
console.log(state.metadata.author.name); // "Engineer"

// The huge TypedArray shares the underlying memory reference
console.log(clonedState.hugeBuffer === state.hugeBuffer); // true

Key Considerations