Reputation: 27311
Background (why Object.update()
or jQuery.extend()
is not a solution, and why this is not a duplicate of the other how-to-copy-a-javascript-object questions):
This version of copy, using a for-loop
function copy_obj(obj) {
var copy = {};
for (var attr in obj) if (obj.hasOwnProperty(attr)) {
copy[attr] = obj[attr];
}
return copy;
}
only copies the values of the attributes (as does Object.update()
), and fails on an object like:
var myobj = {
_fname: 'fname',
_lname: 'lname',
get fullname() { return this._fname + ' ' + this._lname; },
set fullname(v) { this._fname = v; }
};
i.e.
myobj.fullname === 'fname lname' // true
myobj.fullname = 'Anton'
myobj.fullname === 'Anton lname' // true
while
var mycopy = copy_obj(myobj);
mycopy.fullname === 'fname lname' // true
mycopy.fullname = 'Anton'
mycopy.fullname === 'Anton' // oops!
Question: I've figured out that I need to use getOwnPropertyNames/Descriptor to get/set the descriptors, but I haven't found any simple way to determine if something is just a plain value that can be copied directly -- what goes into the if-statement:
function copy_obj2(obj) {
var mycopy = {};
Object.getOwnPropertyNames(obj).forEach(function (prop) {
var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
if (/* descriptor is a plain value */) {
mycopy[prop] = obj[prop];
} else {
Object.defineProperty(mycopy, prop, descriptor);
}
});
return mycopy;
}
(I'm only interested in copying object-like values, not Arrays or other basic types).
Upvotes: 6
Views: 1494
Reputation:
You don't need to worry about the type of descriptor; whether it's a data-type descriptor or an accessor-type descriptor, you can just use it as is to create the property on the target object.
function copy_obj2(obj) {
var mycopy = {};
Object.getOwnPropertyNames(obj).forEach(function (prop) {
var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
Object.defineProperty(mycopy, prop, descriptor);
});
return mycopy;
}
But you could also use getOwnPropertyDescriptors
, with defineProperties
:
function copy_obj2(obj) {
return Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
}
If you also want to include the prototype in the clone, then:
function copy_obj2(obj) {
return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
}
However, getOwnPropertyDescriptors
is not available in IE. A polyfill is suggested here.
Upvotes: 3