Copy

Copy or deep clone a value to an arbitrary depth.

Usage

var copy = require( '@stdlib/utils/copy' );

copy( value[, level] )

Copy or deep clone an input value to an arbitrary depth. The function accepts both objects and primitives.

// Primitives...
var out = copy( 'beep' );
// returns 'beep'

// Objects...
var value = [
    {
        'a': 1,
        'b': true,
        'c': [ 1, 2, 3 ]
    }
];
out = copy( value );
// returns [ { 'a': 1, 'b': true, 'c': [ 1, 2, 3 ] } ]

var bool = ( value[0].c === out[0].c );
// returns false

// Error object...
var err1 = new TypeError( 'beep' );

var err2 = copy( err1 );
// returns <TypeError>

The default behavior returns a full deep copy of any object. To limit the copy depth, set the level option.

var value = [
    {
        'a': 1,
        'b': true,
        'c': [ 1, 2, 3 ]
    }
];

// Trivial case => return the same reference
var out = copy( value, 0 );
// returns [ { 'a': 1, 'b': true, 'c': [ 1, 2, 3 ] } ]

var bool = ( value[0] === out[0] );
// returns true

// Shallow copy:
out = copy( value, 1 );

bool = ( value[0] === out[0] );
// returns false

bool = ( value[0].c === out[0].c );
// returns true

// Deep copy:
out = copy( value, 2 );

bool = ( value[0].c === out[0].c );
// returns false

Notes

  • List of supported values/types:

  • List of unsupported values/types:

    • DOMElement: to copy DOM elements, use element.cloneNode().
    • Symbol
    • WeakMap
    • WeakSet
    • Blob
    • File
    • FileList
    • ImageData
    • ImageBitmap
    • ArrayBuffer
  • The implementation can handle circular references.

  • If a Number, String, or Boolean object is encountered, the value is cloned as a primitive. This behavior is intentional. The implementation is opinionated in wanting to avoid creating numbers, strings, and booleans via the new operator and a constructor.

  • For objects, the implementation only copies enumerable keys and their associated property descriptors.

  • The implementation only checks whether basic Objects, Arrays, and class instances are extensible, sealed, and/or frozen.

  • functions are not cloned; their reference is copied.

  • The implementation supports custom error types which are Error instances (e.g., ES2015 subclasses).

  • Support for copying class instances is inherently fragile. Any instances with privileged access to variables (e.g., within closures) cannot be cloned. This stated, basic copying of class instances is supported. Provided an environment which supports ES5, the implementation is greedy and performs a deep clone of any arbitrary class instance and its properties. The implementation assumes that the concept of level applies only to the class instance reference, but not to its internal state.

    function Foo() {
        this._data = [ 1, 2, 3, 4 ];
        this._name = 'bar';
        return this;
    }
    
    var foo1 = new Foo();
    var foo2 = copy( foo );
    
    var bool = ( foo1._name === foo2._name );
    // returns true
    
    bool = ( foo1._data === foo2._data );
    // returns false
    
    bool = ( foo1._data[0] === foo2._data[0] );
    // returns true
    

Examples

var randu = require( '@stdlib/random/base/randu' );
var Int32Array = require( '@stdlib/array/int32' );
var copy = require( '@stdlib/utils/copy' );

var arr = [
    {
        'x': new Date(),
        'y': [ randu(), randu() ],
        'z': new Int32Array( [ 1, 2, 3, 4 ] ),
        'label': 'Beep'
    },
    {
        'x': new Date(),
        'y': [ randu(), randu() ],
        'z': new Int32Array( [ 3, 1, 2, 4 ] ),
        'label': 'Boop'
    }
];


// Perform a full deep copy:
var out = copy( arr );

var bool = ( arr[ 0 ] === out[ 0 ] );
// returns false

bool = ( arr[ 1 ].y === out[ 1 ].y );
// returns false


// Perform a shallow copy:
out = copy( arr, 1 );

bool = ( arr[ 0 ] === out[ 0 ] );
// returns true

bool = ( arr[ 1 ].z === out[ 1 ].z );
// returns true