Skip to content
Fix Code Error

How do I correctly clone a JavaScript object?

March 13, 2021 by Code Error
Posted By: Anonymous

I have an object x. I’d like to copy it as object y, such that changes to y do not modify x. I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties. This isn’t a problem, since I’m copying one of my own literal-constructed objects.

How do I correctly clone a JavaScript object?

Solution

To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object’s prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don’t know about? In that case, you will copy attributes you shouldn’t, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.

In addition to non-enumerable attributes, you’ll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object’s prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object’s attributes. I think __proto__ might be specific to Firefox’s JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don’t know of any way to discover it automatically.

Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object’s prototype is Object, then simply creating a new general object with {} will work, but if the source’s prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren’t enumerable in the first place. One solution might be to call the source object’s constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as a hidden member:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "nd2 = " + d2.toString());
}, 5000);

The date string for d1 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don’t think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!

When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like the following:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn’t more than one reference to the same data in the object. For example:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don’t assume that it will just work for anything you throw at it.

Answered By: Anonymous

Related Articles

  • SQL find sum of entries by date including previous date
  • How do I keep only the first map and when the game…
  • What are access specifiers? Should I inherit with…
  • typescript - cloning object
  • What is the copy-and-swap idiom?
  • How to "properly" create a custom object in JavaScript?
  • What is The Rule of Three?
  • How to use 2 columns as "key" to get MAX value of…
  • How does JavaScript .prototype work?
  • What is the 'new' keyword in JavaScript?
  • python 3.2 UnicodeEncodeError: 'charmap' codec can't…
  • What are the undocumented features and limitations…
  • How to Deep clone in javascript
  • How do I include certain conditions in SQL Count
  • Base type is not converting to derived type yet can…
  • Construction and ordering of nested and template if=…
  • For-each over an array in JavaScript
  • Smart way to truncate long strings
  • How do I remove single children in a tree?
  • Get the name of an object's type
  • How to create own inheritable class with underscore
  • What are the nuances of scope prototypal /…
  • Copy a variable's value into another
  • Pandas Merging 101
  • What's the difference between git clone --mirror and…
  • How to filter a svelte store using a dynamic filter
  • TypeScript metadata reflection references other…
  • Can't install via pip because of egg_info error
  • What is your most productive shortcut with Vim?
  • SQL / Teradata - How can I get the most recent…
  • Is it safe to shallow clone with --depth 1, create…
  • data.table vs dplyr: can one do something well the…
  • Dynamically allocating an array of objects
  • Polymer 1.0 Trying to make a splitter which works…
  • List changes unexpectedly after assignment. How do I…
  • How does PHP 'foreach' actually work?
  • How to set HTML5 required attribute in Javascript?
  • How to make onClick card to move to another page in react
  • Svelte Derived Store atomic / debounced updates
  • How do I clone a single branch in Git?
  • Click button copy to clipboard using jQuery
  • Coffeescript class extend more bloat than Backbone extend
  • Subscribe to a doc using Svelte / RxJs / RxFire. How…
  • How to use html template with vue.js
  • Git merge with force overwrite
  • Checkout another branch when there are uncommitted…
  • Using underscore.js to copy backbone.js Model…
  • What is move semantics?
  • Oracle manage historical info and new entries
  • How to select rows based on two columns creating an…
  • Deep Cloning Backbone.js Models
  • Virtual member call in a constructor
  • How to recover stashed uncommitted changes
  • Git Using Remote Branch
  • Lodash .clone and .cloneDeep behaviors
  • Pip3 is unable to install requirements.txt during…
  • Virtual/pure virtual explained
  • Adobe XD to responsive html
  • Google Script AddRecord Function and For Loops
  • Deep cloning objects
  • explain backbone object and class creation pattern
  • How do SO_REUSEADDR and SO_REUSEPORT differ?
  • When should I use derived in Svelte custom stores?
  • Refresh bindings in parent view when parent value is…
  • Derived class nested inside base class - C++
  • Four Backbone.js Model questions
  • Is the destructor of a derived class virtual…
  • Unexpected literal in error position of callback in Vue.JS
  • Running multi-stacker docker built docker image…
  • does Backbone.Models this.get() copy an entire array…
  • Why does Git say my master branch is "already up to…
  • Understanding dict.copy() - shallow or deep?
  • Evaluating a mathematical expression in a string
  • Active tab issue on page load HTML
  • Jquery fadeToggle Trouble
  • How to group partially matching rows in a dataframe?
  • What is Ember RunLoop and how does it work?
  • How to Update Database from Assets Folder in App
  • How do I do a drag and drop using jQuery to move…
  • Clone private git repo with dockerfile
  • JSONAPI serialize nested hasMany relationships
  • Best way to communicate between instances of the…
  • What does this symbol mean in JavaScript?
  • Difference between xcopy and robocopy
  • Aurelia UX showcase app fails to load
  • OpenCV Object detection with Feature Detection and…
  • Aurelia app is not launched using au run cli command
  • Average values between two dates by group
  • Class vs. static method in JavaScript
  • What is the difference between Numpy's array() and…
  • Is there a way to use nextjs with docker and nginx
  • onClick of a btn i want to get it's sibling(input…
  • How to implement the factory method pattern in C++ correctly
  • What is the most efficient way to deep clone an…
  • I want to solve the javascript OOP problems, but my…
  • Tools to selectively Copy HTML+CSS+JS From A…
  • How do I merge two dictionaries in a single…
  • Dividing each row by the previous row
  • Could `Cell` in Rust be safely used on `Rc` specifically?
  • Pivoting a defined number of rows into columns…

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

How do I update Node.js?

Next Post:

Change column type in pandas

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

.net ajax android angular arrays aurelia backbone.js bash c++ css dataframe ember-data ember.js excel git html ios java javascript jquery json laravel linux list mysql next.js node.js pandas php polymer polymer-1.0 python python-3.x r reactjs regex sql sql-server string svelte typescript vue-component vue.js vuejs2 vuetify.js

  • you shouldn’t need to use z-index
  • No column in target database, but getting “The schema update is terminating because data loss might occur”
  • Angular – expected call-signature: ‘changePassword’ to have a typedeftslint(typedef)
  • trying to implement NativeAdFactory imports deprecated method by default in flutter java project
  • What should I use to get an attribute out of my foreign table in Laravel?
© 2022 Fix Code Error