0

I've written my own deepCopy-Function, that is able to copy and Object:

function deepCopyObj(object){
    if(object == null || typeof(object) != 'object'){
        return object;
    }

    var copy = object.constructor();   //This line makes some troubles
    for (var attr in object) {
        if(object.hasOwnProperty(attr) && typeof(object[attr]) !== "undefined") {
            copy[attr] = deepCopyObj(object[attr]);
        }
    }
    return copy;
}

This code always worked fine - until now: Sometimes, when I want to copy an object, the command var copy = object.constructor(); returns undefined.

What is the reason for that? When I print object to the console, it gives me the correct output.

Notice, that my code sometimes use delete object.anAttribute; to remove functions - but I don't know if this can be the reason.

maja
  • 17,250
  • 17
  • 82
  • 125
  • 4
    You can't rely on constructor functions to be callable like that. You also can't rely on them not to require one or more parameters. These are among the reasons that it's challenging to create a general-purpose "foolproof" JavaScript deep-copy facility :) – Pointy Jan 24 '14 at 15:54
  • There are two reasons this might be the case: 1. Somewhere else in your code you have run a `delete your_object.constructor` before you pass it into `deepCopyObj`. - *or* - 2. You are passing in an object that does not *have* a constructor, e. g. an object created with `Object.create(null)`. The only way to know for sure is to break on that error and see what the object is and walk back up the stack to figure out where it came from. – Sean Vieira Jan 24 '14 at 15:55
  • Here's a more robust version of the above code: http://stackoverflow.com/a/13333781/74757. It will handle constructor parameters and there's a second version that handles cyclical references. – Cᴏʀʏ Jan 24 '14 at 15:56
  • I know the Object - it's called parserAPI, and the functions and attributes inside it always change. I add them via `parserAPI.foo = function(){...}`, and I delete them with my delete-command which is hardcoded - so I never touched the constructor. – maja Jan 24 '14 at 15:58
  • 1
    You really can't "handle" constructors that require parameters because the parameters can be captured in a closure that's used by functions exposed as object properties. – Pointy Jan 24 '14 at 15:59
  • @Cory your deepCopy-Function works - however, I wonder what the problem was - I'll investigate it – maja Jan 24 '14 at 16:16

2 Answers2

2

Also note that your deepCopyObj() will only copy enumerable properties of the source object.

var obj = {name: "Andrew", lastname:"Smith"};
Object.defineProperty(obj,"id",{
    enumerable:false,
    configurable:true,
    writeable:true,
    value:"1"
});
var newObj = deepCopyObj(obj);

newObj.id returns undefined

See How do I correctly clone a JavaScript object? for further explanation

Community
  • 1
  • 1
0

The problem was simple: The object I copied had some parameters.

The deepCopy-Function doesn't need to be bullet-proof, so I only added special treatment for this kind of object:

var copy;
if(object instanceof ParserAPI){        //only for debugging-purposes
    var constructorParams = object.getConstructorParameters();
    copy = new ParserAPI(constructorParams[0], constructorParams[1]);
}else{
    copy = object.constructor();
}
if(typeof(copy) == "undefined"){        //pls pls pls, don't appear
    throw new Error("deepCopyObj failed");  
}

And in the ParserAPI-Object, which needs to be copied, I have this code now:

function ParserAPI (_input, _reporter){
    var input=_input, reporter=_reporter;

    self.getConstructorParameters = function(){
        return [self.input, self.reporter];
    }
    ...
}
maja
  • 17,250
  • 17
  • 82
  • 125
  • 1
    Don't do this `instanceof`-testing in a generic method. Instead, give every `ParserApi` instance a `.copy()` or `.clone()` method, which can treat them special. In `deepCopy`, only check the interface by `typeof object.clone == "function"` and call it when it's available. – Bergi Jan 31 '14 at 09:05
  • @Bergi that's an awesome idea, I'll do that – maja Jan 31 '14 at 10:35