DEV Community

Discussion on: Be aware when cloning objects in JavaScript! 👯‍♀️

Collapse
 
lexlohr profile image
Alex Lohr • Edited

Yes and no. JSON.parse/stringify will serialize and deserialize the data. Some types are not supported and cyclic references will also lead to errors. But putting both variants together, you'll get:

const clone = (obj, map) => {
    if (!map) {
        map = new Map();
    }
    if (obj === null || typeof (obj) !== 'object')
        return obj;

    if (map.has(obj))
        return map.get(obj);

    const temp = obj instanceof Date
        ? new obj.constructor()
        : obj.constructor();

    map.set(obj, temp);

    for (let key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            temp[key] = clone(obj[key], map);
        }
    }
    return temp;
}
Enter fullscreen mode Exit fullscreen mode

Now that works for all usual types. It will fail for some of the modern things like Map/WeakMap etc.

Thread Thread
 
dailydevtips1 profile image
Chris Bongers

It all goes deeper than you would think, wonder why they don't build this in ECMAScript...
Seems like a pretty "basic" function to deepclone right?

Thread Thread
 
lexlohr profile image
Alex Lohr

I guess libraries like lodash made it convenient enough to have that functionality, so there was no reason to provide a native method. Also, since the native types are currently a moving target, it seems prudent to wait until it has stabilized enough to make such a functionality feasible. That shouldn't stop anybody from making your own proposal to the TC39 committee to include Object.clone(obj) into a future ECMAScript standard, though.

Thread Thread
 
lexlohr profile image
Alex Lohr

I guess I'll make a proposal myself, if nobody beats me to it. Here's the polyfill:

if (typeof Object.clone !== "function") {
  const clone = (obj, map) => {
    if (obj === null || typeof obj !== "object" || obj instanceof WeakMap)
      return obj;

    if (map.has(obj)) return map.get(obj);

    const temp =
      obj instanceof TypedArray
        ? new obj.constructor(obj.length)
        : new obj.constructor();

    map.set(obj, temp);

    if (obj instanceof TypedArray) {
      temp.set(obj.map((value) => clone(value, map)));
    } else if (obj instanceof Map) {
      obj.forEach((value, key) => temp.set(key, clone(value, map)));
    } else if (obj instanceof Date) {
      temp.setTime(obj.getTime());
    } else {
      for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          temp[key] = clone(obj[key], map);
        }
      }
    }
    return temp;
  };
  Object.clone = (obj) => clone(obj, new Map());
}
Enter fullscreen mode Exit fullscreen mode

I also did a small test suite and a documentation. I'll work a bit on it and then release it to the public.

Thread Thread
 
dailydevtips1 profile image
Chris Bongers

Wow Alex, Your a speedy guy!
Nice work, happy to test with you 👀

Thread Thread
 
lexlohr profile image
Alex Lohr • Edited

Here's the initial draft: github.com/atk/object-clone-proposal. Feedback is appreciated.