skip to Main Content

For usual object using string as key, we can use JSON.parse(JSON.stringify(myObject)).
Another
How can I deep clone an object using Symbol() as keys ?

Note: structuredClone({[Symbol()]: 42}) returns {} which is not usable either

2

Answers


  1. Use Object.getOwnPropertySymbols to iterate and copy symbols.

    You can improve the code by copying property descriptors instead of simple property assignment.

    enter image description here

    const symbol = Symbol();
    const symbol2 = Symbol();
    const obj = {[symbol]: 42, child: {[symbol2]: 11}, arr: [{[Symbol()]: 'foo'}, 2, 3]};
    
    const cloneObjectWithSymbols = (obj) => {
    
      if(obj?.constructor?.name === 'Object'){
        
        const out = {};
        
        for(const symbol of Object.getOwnPropertySymbols(obj)){
          const prop = Object.getOwnPropertyDescriptor(obj, symbol);
          Object.defineProperty(out, symbol, prop);
        }
        
        for(const k in obj){
          out[k] = cloneObjectWithSymbols(obj[k]);
        }
        return out;
      
      } 
      
      return Array.isArray(obj) ? obj.map(cloneObjectWithSymbols) : obj;
    
    };
    
    const cloned = cloneObjectWithSymbols(obj);
    console.log(cloned[symbol], cloned.child[symbol2]);
    console.log(cloned);
    Login or Signup to reply.
  2. One approach would be to iterate through all properties and Symbols and copy them to a new object(since structuredClone will not be able to clone Symbols and methods1).

    Pseudocode:
    # 1. Base case of recursive call: if provided obj is null or not an object return the value as is.
    # 2. initialize cloned object as object or array based on obj type.
    # 3. for(all the own property keys of the input obj)
        # 3.1. if current prop is an object and !null apply recursive call. why? since it is nested.
        # 3.2. else it will be some primitive value which suggest that we define a new property on clonedObj with its descriptors and value.
    # 4. return clonedObj
    
    function customDeepCloneWithSymbols(obj) {
        // Base Case
        if (obj === null || typeof obj !== 'object') {
            return obj;
        }
        
        let clonedObj = Array.isArray(obj) ? [] : {};
        // Faith
        for (let prop of Reflect.ownKeys(obj)) {
            if (typeof obj[prop] === 'object' && obj[prop] !== null) {
                clonedObj[prop] = customDeepCloneWithSymbols(obj[prop]);
            } else {
                Reflect.defineProperty(clonedObj, prop, Reflect.getOwnPropertyDescriptor(obj, prop));
              // or
              // Object.defineProperty(clonedObj, prop, Object.getOwnPropertyDescriptor(obj, prop));
            }
        }
    
        return clonedObj;
    }
    

    SIDENOTE:

    The clone and cloneDeep functions provided by lodash clone Symbols in case you’re flexible for using a library to achieve your purpose.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search