0

I'm writing my clone function in javascript.

That function recursively clone object (avoiding circular reference) and seem work well, but if the object (at some level) has a built in object reference, when i try to access to some property of cloned object (that appear correct) i've a type error.

This is my simple example with datetime (i know there are more efficent way for clone datetime object, but that is not my concern, what i need to do is clone a generic built object, datetimes are only an example) that semplificate my real clone method to a method that only shallow copy function and array but deep copy object (except for internal array and function) and not avoid circular reference. This is only a dimostration of the error (the same error i obtain with the complete clone function).

CODE :


        var date = new Date () ;
        var dateProto = date.__proto__ ;

        var cloned = {} ;
        var clonedProto = {} ;

        function clone ( obj )
        {
            if ( obj instanceof Array )
                return [] ;

            if ( obj instanceof Function )
                return obj ;

            if ( obj instanceof Object )
            {
                var result = {} ;
                var elems = Object.getOwnPropertyNames(obj) ;
                var len = elems.length ;
                for ( var i = 0 ; i < len ; i++  )
                {
                    var prop = elems[i] ;
                    var elem = obj[prop] ;

                    result [ prop ] = clone ( elem ) ;
                }

                return result ;
            }

            return obj ;
        }

        cloned = clone ( date ) ;
        clonedProto = clone ( dateProto ) ;

        cloned.__proto__ = clonedProto ;

        alert ( cloned.getDay() );

But this cause this type error when try to access to getDay method : Uncaught TypeError: this is not a Date object.

But i still do not understand why, cloned appear like date object, i know that methods are shared, i'am expecting strange behaviour when invoked (method refer to the object called "date", so the object internal state is "date" not "cloned") but not an error.

So why this error?

Thanks for help and sorry for my bad english.


Edited

According with the new idea suggested (both form RobG and form article posted by jfriend00 in the comment) to me i rewrite the clone function i that way.

                    function clone ( obj )
        {
            if ( obj instanceof Array )
                return [] ;

            if ( obj instanceof Function )
                return obj ;

            if ( obj instanceof Object )
            {
                var result = new obj.constructor() ;

                result.__proto__ = clone ( obj.__proto__ ) ;

                var elems = Object.getOwnPropertyNames(obj) ;
                var len = elems.length ;
                for ( var i = 0 ; i < len ; i++  )
                {
                    var prop = elems[i] ;
                    var elem = obj[prop] ;

                    result [ prop ] = clone ( elem ) ;
                }

                return result ;
            }

            return obj ;
        }

Now seems to work how i expected, but i do not understand why this piece of code : var result = new obj.constructor() ; make the difference.

Thanks a lot for the help.

Raji
  • 11
  • 3
  • To clone a Date object, you'll need to use the Date constructor (note that all objects are instances of Object, including functions and arrays). And that can be done using `var cloneDate = new Date(+originalDate);` unless you have added some other properties directly to the originalDate object. They can be copied using your for..in strategy on own properties. – RobG Jun 04 '13 at 00:44
  • @RobG you gave me an idea, dinamically retrieve constructor of each object i would clone and recursively clone prototype. If i do that it work and i suppose prototype appear completly cloned. But i don't undestand why and if it's correct. Can you explain it to me? – Raji Jun 05 '13 at 01:41
  • The constructor property can be modified (re–assigned) so if you use it, you just hope it points to the right object. If you create a new instance, then it already has the "right" `[[Prototype]]`. All that's left is to copy over own properties of the original. But that will not clone "private" members that may have been added using closures. So it's impossible to make a general "clone" function that works in every case, however your approach will work in a restricted context (constructor is correct, no private properties, no user defined non–enumerable own properties, etc.). – RobG Jun 05 '13 at 02:09
  • Thank's a lot for the help, both RobG and jfriend00. I suppose i've solved (and understand) my issue now ^_^ – Raji Jun 05 '13 at 09:21

1 Answers1

1

When your object is some other type of object besides a plain object (like a Date object), you aren't creating a cloned Date object, you're creating a plain object.

Further when you copy properties to the cloned object, you are only copying properties directly on the object, not properties in the prototype chain as that is what Object.getOwnpPropertyNames() returns. Thus your cloned Date object has none of the Date methods.

Per RobG's suggestion, add this first line right before the return:

result.__proto__ = obj.__proto__;
return result ;
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Yes, the OP is using the clone function to copy properties of the prototype (using the non–standard `__proto__`), which aren't enumerable. I thought he'd just do `clone.__proto__ = obj.__proto__`. – RobG Jun 04 '13 at 00:40
  • I know that my result is not properly a Date object, but just act like ones of them (and for me is enought). – Raji Jun 04 '13 at 06:09
  • I know that my result is not properly a Date object, but just act like ones of them (and for me is enought). I've debugged the object in this example and in the real case (where a more complex function navigate through the object tree and prototype too) and cloned object show the same schema of the original Date object. If you see in the example (for not complicating too much the recursion) i've explicitly cloned the prototype of the Date object and assigned it to Date's clone. So in debugger my clone appear completly (the schema) equal to original, but do not work. (sorry for double post) – Raji Jun 04 '13 at 06:16
  • @RobG i know there is better way for clone Date object, but is just an example. In real case the object that i would clone may have some property that are RegExp , Date , Error or any other builtin object; and i need to be able to copy all of them (so i need a generic method for cloning builtin object). – Raji Jun 04 '13 at 06:19
  • @Raji - did you try Rob's suggestion of assigning the prototype? There are hundreds of posts about decent ways to clone JS objects. Here's [one](http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object). – jfriend00 Jun 04 '13 at 06:48
  • but i would clone prototype too, so i'm sure i could modify all i want in cloned object. Or is not possibile ? If prototype and constructor does not matter someone could me explain why? (with does not matter i mean that do not restrict the programmer freedom when he using cloned object) Thanks a lot (anyway seems that with prototype work) – Raji Jun 05 '13 at 01:27
  • @Raji - There is no need to clone the prototype if you intend for the clone to be the same type of object as the original (because they should have the same prototype if they are the same type of object). You can assign any override property you want to the cloned object without having to change the prototype. – jfriend00 Jun 05 '13 at 06:03