Object.clone - not quite a proposal

# Jeremy Darling (9 years ago)

I'm horrid with proposals, but this seems like a missing hole and I couldn't find a previous mention of it.

Looking at the Object support methods it seems that the only thing that doesn't exist is a (simple) way to clone an object and all of its members.

Ideally would allow creation of immutable and/or deep/shallow clones of existing Object/Array/RegExp/Date/...

Basic shim (most likely not perfect):

Object.clone = function(src){ if(!src || typeof(src) !== 'object'){ return src; } if(src instanceof Date){ return new Date(src); } if(src instanceof Array){ return src.map((item)=>clone(item)); } if(src instanceof RegExp){ return new RegExp(src); } var temp = (src.prototype)?Object.create(src.prototype):new src.constructor(); Object.keys(src).forEach((key)=>{temp[key] = Object.clone(src[key]);}); return temp; };

Thoughts?

# Andrea Giammarchi (9 years ago)

return src.map((item)=>clone(item));

probably return src.map(Object.clone); would do, if it accepts only one argument.

It seems that HTML nodes are missing (through document.createXXX) and it relies on instanceof which works for me but it doesn't as proposal 'cause incompatible cross realms.

Last, but not least, one does not simply assign properties in JavaScript. If you want to clone you don't want to miss possible get/set accessors in the process, you don't want to miss getOwnPropertyNames and getOwnpropertySymbols neither, so the last loop doesn't work as proposal.

// simplified version for regular objects only
// basically just the correct substitute of your last temp logic
Object.clone = function (src) {
  return Object.setPrototypeOf(
    Reflect.ownKeys(src).reduce((o, k) => {
      var d = Object.getOwnPropertyDescriptor(src, k);
      if (
        o.hasOwnProperty.call(d, 'value') &&
        /function|object/.test(typeof d.value)
      ) d.value = Object.clone(d.value);
      return Object.defineProperty(o, k, d);
    }, {}),
    Object.getPrototypeOf(src)
  );
};

That should be kinda better, IMO, and if you need to test with Reflect you can:

var Reflect = Reflect || {
  ownKeys: (o) => Object.getOwnPropertyNames(o).concat(
    Object.getOwnPropertySymbols(o)
  )
};

Best

# Claude Pache (9 years ago)

Le 24 sept. 2015 à 19:58, Andrea Giammarchi <andrea.giammarchi at gmail.com> a écrit :

Last, but not least, one does not simply assign properties in JavaScript. If you want to clone you don't want to miss possible get/set accessors in the process, you don't want to miss getOwnPropertyNames and getOwnpropertySymbols neither, so the last loop doesn't work as proposal.

// simplified version for regular objects only
// basically just the correct substitute of your last temp logic
Object.clone = function (src) {
  return Object.setPrototypeOf(
    Reflect.ownKeys(src).reduce((o, k) => {
      var d = Object.getOwnPropertyDescriptor(src, k);
      if (
        o.hasOwnProperty.call(d, 'value') &&
        /function|object/.test(typeof d.value)
      ) d.value = Object.clone(d.value);
      return Object.defineProperty(o, k, d);
    }, {}),
    Object.getPrototypeOf(src)
  );
};
Object.clone = function (src) {
    return Object.create(Object.getPrototypeOf(src), Object.getOwnPropertyDescriptors(src))
}

where the definition of Object.getOwnPropertyDescriptors() is left as an exercise to the reader.

# Jeremy Darling (9 years ago)

The code was only meant to be a better explanation of the goal, but yes it would have to take into account getters, setters, etc...

Actually it was a copy/paste edit of some code we use for cloning (deserialized) JSON objects for API reproduction. There is an omit stage that was removed and since it was originally designed around JSON structures there is no built in support for custom properties, getters, setters.

DOM nodes I'm not sure how you would account for. In all actuality I would think they would throw an error, but maybe they shouldn't. Magically appearing textboxes could be a bad thing :D

# Jordan Harband (9 years ago)
# Andrea Giammarchi (9 years ago)

Claude, you know that's actually my proposal, right? :P gist.github.com/WebReflection/9353781

But yeah, that's the spirit!

# Andrea Giammarchi (9 years ago)

Also, one reason I haven't used that, if you clone descriptors you do a shallow copy, not a deep one ;-)

# Isiah Meadows (9 years ago)

I'll also point out that cloning DOM elements generally won't work as intended because DOM implementations sometimes tack on hidden properties that aren't even seen by the engine. Also, I know that V8 adds hidden properties to objects, which would have to be transferred as well.

And this is where persistent data structures become useful... (I recently dropped Mori into a project because I was dealing with a ton of immutable data, but I still wanted it to be lightweight, and I didn't need all the bells and whistles of Immutable.js.)