Skip to content

Instantly share code, notes, and snippets.

@AKST
Last active January 29, 2018 12:29
Show Gist options
  • Save AKST/0feca694a9b6741311999fa75229bbcd to your computer and use it in GitHub Desktop.
Save AKST/0feca694a9b6741311999fa75229bbcd to your computer and use it in GitHub Desktop.
A function for deep copying objects, that makes the assumption that the constructors of these objects are stateless.
function deepcopy (value) {
// if it's anything other than an object
// than let immediately return it.
switch (typeof value) {
case 'object': break;
default: return value;
}
// If it's a null let also return that.
if (value === null) return value;
// This is probably more efficient... keyword being 'probably' lol
if (Array.isArray(value)) return value.map(deepcopy);
// this kind of makes the assumption that the constructors
// for these objects are stateless, and initialising an object
// this way is safe...
const prototype = Object.getPrototypeOf(value);
const copy = Object.create(prototype);
const descriptors = Object.getOwnPropertyDescriptors(value);
Object.keys(descriptors).forEach(key => {
const descriptor = descriptors[key];
// anything that is set in the class via `this.blah = blah`
// will be deepcopied in this way. As we know
if ('value' in descriptor) {
const newDesciptor = Object.assign(
{},
descriptor,
{ value: deepcopy(value[key]) }
);
Object.defineProperty(copy, key, newDesciptor);
}
else {
Object.defineProperty(copy, key, descriptor);
}
});
return copy;
}
@AKST
Copy link
Author

AKST commented Jan 29, 2018

If your class or objects doesn't have any stateful closures the function above works fine, but there is one way to break this and that is for an object to use closures, like the following

const factory = function constructor () {
  let count = 0;
  
  return {
    increment () {
      count += 1;
    },
    
    get count () {
      return count;
    }
  };
};

const a = factory();
const b = deepcopy(b);

console.log(a.count); // 0
console.log(b.count); // 0

b.increment();

console.log(a.count); // 1
console.log(b.count); // 1

I think the main take away from this is, you really can't deep copy objects in a non purely functional language (where deep copying is irrelevant anyways). Now if all you're doing is deep copying vanilla javascript objects you're fine, but you should simply the function above, as I'm not to sure how efficient Object.getPrototypeOf is 🤔 I imagine Object.clone is fine, but idk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment