O'Reilly

Clone Anything with JavaScript

By on  

One topic or concept that causes confusion when developers start with JavaScript is the idea of passing objects by reference;  for example, setting two variables equal to the same object actually creates a reference to that same object.  Sending an object to a function and modify that argument within the function actually modifies the original object.  Sometimes we'd prefer to send around a clone of something, a date, array, or maybe an object literal.  The Dojo Toolkit provides an excellent method for cloning just about anything.  Even better is that the functionality is easy to pull out of Dojo for your own toolkit.

The JavaScript

The clone method will deep clone nodes, object literals, arrays, dates, regular expressions, and generic objects:

function clone(src) {
	function mixin(dest, source, copyFunc) {
		var name, s, i, empty = {};
		for(name in source){
			// the (!(name in empty) || empty[name] !== s) condition avoids copying properties in "source"
			// inherited from Object.prototype.	 For example, if dest has a custom toString() method,
			// don't overwrite it with the toString() method that source inherited from Object.prototype
			s = source[name];
			if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))){
				dest[name] = copyFunc ? copyFunc(s) : s;
			}
		}
		return dest;
	}

	if(!src || typeof src != "object" || Object.prototype.toString.call(src) === "[object Function]"){
		// null, undefined, any non-object, or function
		return src;	// anything
	}
	if(src.nodeType && "cloneNode" in src){
		// DOM Node
		return src.cloneNode(true); // Node
	}
	if(src instanceof Date){
		// Date
		return new Date(src.getTime());	// Date
	}
	if(src instanceof RegExp){
		// RegExp
		return new RegExp(src);   // RegExp
	}
	var r, i, l;
	if(src instanceof Array){
		// array
		r = [];
		for(i = 0, l = src.length; i < l; ++i){
			if(i in src){
				r.push(clone(src[i]));
			}
		}
		// we don't clone functions for performance reasons
		//		}else if(d.isFunction(src)){
		//			// function
		//			r = function(){ return src.apply(this, arguments); };
	}else{
		// generic objects
		r = src.constructor ? new src.constructor() : {};
	}
	return mixin(r, src, clone);

}

The code provided by Dojo also has the ability to clone functions, but that ability is disabled for performance reasons.  I've placed the mixin function within clone itself, but that can also be defined at the same level and you can use mixin as a general function for merging objects.  This method, of course, is just one of a thousand helpful snippets you can find within the Dojo Toolkit!

Track.js Error Reporting

Recent Features

  • CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • 5 Ways that CSS and JavaScript Interact That You May Not Know About

    CSS and JavaScript:  the lines seemingly get blurred by each browser release.  They have always done a very different job but in the end they are both front-end technologies so they need do need to work closely.  We have our .js files and our .css, but...

Incredible Demos

  • Introducing MooTools ScrollSidebar

    How many times are you putting together a HTML navigation block or utility block of elements that you wish could be seen everywhere on a page? I've created a solution that will seamlessly allow you to do so: ScrollSidebar. ScrollSidebar allows you...

  • CSS Vertical Centering

    Front-end developing is beautiful, and it's getting prettier by the day. Nowadays we got so many concepts, methodologies, good practices and whatnot to make our work stand out from the rest. Javascript (along with its countless third party libraries) and CSS have grown so big, helping...

Discussion

  1. wiky

    There are an idea to clone an object(or array) by JSON method without deep traversal:

    function clone (src) {
        return JSON.parse(JSON.stringify(src));
    }
    

    But it only supports to clone Object and need JSON support.

  2. Thanks David… I’m sure at some point this might be handy… ditto WIKY… that’s a great *simple* approach… I wonder if there’s any issues with that? (other than lack of support on legacy browsers)

  3. I implemented something similar on amd-utils/lang/clone. It handles RegExp flags properly (missing on the snippet above) and avoid IE don’t enum bug. I also find the code more readable ;-D

    Element.cloneNode(true) is so easy to type that I don’t think it’s worth the overhead on the clone() method, I would definitely keep it just to JavaScript natives.

    Cheers.

  4. Rasmus Fløe

    The dojo source is scary! :(

    The intent is good but…

    I don’t understand why they wouldn’t just use hasOwnProperty instead of having an empty object and checking against that. Yikes. Must have a good reason I guess… :/

    And then there’s the rather naive expectation that typeof would ever return “array”!?

    if(src && (src instanceof Array || typeof src == "array")){

    • I am seeing that now; I’ll bring it up with the Dojo team, as these items are quite odd.

    • OK, the SRC check in that statement was my bad on the translation. The typeof does exist in Dojo, not sure why that’s there.

  5. Paul Bronshteyn

    Looping though the arrays to clone then is slow, using native methods are much faster:

    r = src.concat([]);

    • concat doesn’t do a deep clone (objects and arrays inside of it aren’t cloned).

    • christ almighty

      what you’re describing is not a “deep clone”.

      any non-primitive item (Array, Object, etc) in the Array will be passed as a reference, it will not be a copy, so changing an item in the cloned Array (e.g. popping an item off an Array nested within it) will also alter the original.

      this may or may not be the behaviour you want, but it is important enough for me to come back down to earth and school your sorry @$$ as to why you should think before you comment.

  6. christ almighty

    Without including all the implementation faff, a more sexual solution might look like:

    var clone = function() {
        function mixin( dest, source, copyFunc ) {}
    
        return function clone( src ) {
    
            var type = Object.prototype.toString.call( src ).substring( 8 ).split( ']' )[0].toLowerCase();
        
            switch ( type ) {
                case 'function'  : break;
                case 'array'     : break;
                case 'object'    : 
                    if ( !src.constructor || src.constructor === Object ) {} // <- handles Object.create( null );
                    else return new src.constructor();
                    break;
                case 'regexp'    : break;
                case 'date'      : break;
                case 'undefined' : case 'null' : break;
                default          : 
                    if ( type.indexOf( 'html' ) === 0 ) {}
            }
            return mixin( /*r, src, clone */ );
        };
    }();
    

    switch always looks more sexual, your making the switch, it’s definite, you’re an attractive assertive go getter. A bunch of ifs? too iffy… also, moving the mixin function out of the clone function is mucho nice-u and mean you’re not recreating it each time this method is called!

    jesus, just sayin’….

    • christ almighty

      formatting!!! :P

  7. Hello ! I am Japanese !
    I am Beginner of Javascript.
    This code will do bad?

    var clone = function(o){
    	var Clone = function(o){
    		var self = this;
    		if(o instanceof Array){
    			for(var i = 0,l = o.length;i < l;++ i){
    				self[i] = o[i];
    			}
    		}else{
    			for(var n in o){
    				self[n] = o[n];
    			}
    		}
    	};
    	if((!o)||(typeof(o) !== "object")){
    		return o;
    	}else{
    		o = new Clone(o);
    		return o;
    	}
    };
    
    myObject = {foo:"bar",deep:{func:function(){alert("do something");}}};
    var cloned = clone(myObject);
    alert(cloned.foo) //bar
    myObject.foo = "bbbbbbbb";
    alert(cloned.foo) //bar
    cloned.deep.func(); // do something
    
  8. I was checking briefly the code and I found this line:

    if(src instanceof Array){

    Which is consider not the best way to check if a variable is an Array (it won’t work when dealing with multi-frames).

  9. BitDagger

    @JHUESOS

    What’s the proper way of checking that then?

  10. Francisc

    Hey David,

    Awesome post.
    A question though, should typeof src!="object" be typeof src!=="object"?

    • Me

      typeof will always return a string

  11. Markus Staab

    When jquery is available it is rather easy

    Clone object: clone = jQuery.extend({}, obj);

    Clone array: clone = jQuery.extend([], arr);

  12. Cody

    For arrays, a more traditional way is:

    var arr = [1,2,3];
    var newArr = arr.slice(0);  // zero is optional
    console.log(arr === newArr, newArr);
    

    But this is probably outside of what David is actually aiming to accomplish as his function uses autonomy.

    Thanks David!

  13. André Padez

    Dave, I changed the repo, and a bit of the code:
    (if you can delete my previous comment)
    https://github.com/andrepadez/better-objects

    Object.prototype.clone = function() {
        var theClone;
    
        var traverseObject = function(obj, clone){
            Object.keys(obj).forEach(function(key){
                if(Array.isArray(obj[key])){
                    clone[key] = traverseArray(obj[key])
                } 
                else if(typeof obj[key] === 'object'){
                    clone[key] = {};
                    traverseObject(obj[key], clone[key]);
                } 
                //we don't clone functions for performance reasons
                // else if(typeof obj[key] === 'function'){
                //     clone[key] = eval(obj[key].toString());
                // }
                else {
                    clone[key] = obj[key];
                }
            });
        };
    
        var traverseArray = function(arr){
            var finalArray = [];
            arr.forEach(function(item){
                if(Array.isArray(item)){
                    finalArray.push( traverseArray(item) );
                }
                else if(typeof item === 'object'){
                    var newObj = {};
                    traverseObject(item, newObj)
                    finalArray.push( newObj );
                } 
                else {
                    finalArray.push(item);
                }
            });
            return finalArray;
        }; 
    
        if( Array.isArray(this) ){
            theClone = traverseArray(this);
        } else {
            theClone = {};
            traverseObject(this, theClone);    
        }
        
        return theClone;
    };
    
  14. Robin Cafolla

    Hi David,

    You may want to add the following to allow cloning of TypedArrays

    if(Object.prototype.toString.call(src.buffer) === "[object ArrayBuffer]"){
        return new src.constructor( src );
    }
    
  15. Andy

    You probably don’t want to go implementing your own clone method – just use the NPM clone package https://www.npmjs.com/package/clone which is actively maintained, and handles circular dependencies. However, the npm package may not clone DOM nodes, but you should probably just us the native browser API if you really have to do that. This isn’t a wheel you need to reinvent!

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Convert Video to mp3

    Let's all be honest for a moment:  we've all ... not paid for ... music.  Whether it was via a file sharing app like Kazaa or Napster, or it was downloading and seeding on bittorrent, or maybe even downloading a music video and ripping its audio,...

  • Sort git Branches by Date

    I'll be first person to admit I don't do as much git repository maintenance as I should.  I rarely delete branches which have been merged, so a git branch execution shows me a mile-long list of branches that likely aren't relevant.  The best way to find branches I've recently...

  • Best Tools and Resources for Web Professionals in 2015

    Looking for the right resources to help you satisfy the needs of your clients? On the lookout for the best tools to help you increase your revenue? Searching for the right software to help you improve your business? Well, then you’ve come to the right place....

  • JavaScript Polling

    Polling with JavaScript is one of those ugly but important functions within advanced front-end user experience and testing practices.  Sometimes there isn't the event you can hook into to signify that a given task is complete, so you need to get your hands dirty and simply poll for...

  • OSCON Portland:  Conference Giveaway and Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...