8

Shallow copy and deep copy

A shallow copy creates a new object with a copy of the original object's property values. If the attribute is a primitive type, the value of the primitive type is copied, and if the attribute is a reference type, the memory address is copied. Without a deep copy, one of the objects changes the value of the object, which affects the value of the other object.
Deep copy is to copy an object completely from the memory, open up a new area from the heap memory to store the new object, and modify the new object will not affect the original object.

1. JSON.parse(JSON.stringify(obj))

In general, a deep copy is required for ordinary objects, and this method can be used to perform a deep copy operation, which is the simplest and the least code-intensive deep copy method.

 let a = {a:1,b:2}
let b = JSON.parse(JSON.stringify(a))
a.a = 11
console.log(a)//{a:1,b:2}
console.log(b)//{a:11,b:2}

1.1 Defects of deep and shallow copying of JSON.parse(JSON.stringify(obj))

 let a = {
    name: 'Jack',
    age: 18,
    hobbit: ['sing', {type: 'sports', value: 'run'}],
    score: {
        math: 'A',
    },
    run: function() {},
    walk: undefined,
    fly: NaN,
    cy: null,
    date: new Date()
}
let b = JSON.parse(JSON.stringify(a))
console.log(b)
// {
//     age: 18,
//     cy: null,
//     date: "2022-05-15T08:04:06.808Z"
//     fly: null,
//     hobbit: (3) ["dance", "sing", {…}],
//     name: "Jack",
//     score: {math: "A"},
// }

The key with the value of undefined cannot be obtained; if there is a function in the object, the function cannot be copied; the properties and methods on the prototype chain of the copyObj object cannot be copied; the object is converted into a date string.

2. Ordinary recursive function implements deep copy

 function deepClone(source) {
  if (typeof source !== 'object' || source == null) {
    return source;
  }
  const target = Array.isArray(source) ? [] : {};
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (typeof source[key] === 'object' && source[key] !== null) {
        target[key] = deepClone(source[key]);
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}

2.1 Resolving circular references and symblo types

 function cloneDeep(source, hash = new WeakMap()) {
  if (typeof source !== 'object' || source === null) {
    return source;
  }
  if (hash.has(source)) {
    return hash.get(source);
  }
  const target = Array.isArray(source) ? [] : {};
  Reflect.ownKeys(source).forEach(key => {
    const val = source[key];
    if (typeof val === 'object' && val != null) {
      target[key] = cloneDeep(val, hash);
    } else {
      target[key] = val;
    }
  })
  return target;
}

3. Compatible with multiple data types

 const deepClone = (source, cache) => {
  if(!cache){
    cache = new Map() 
  }
  if(source instanceof Object) { // 不考虑跨 iframe
    if(cache.get(source)) { return cache.get(source) }
    let result 
    if(source instanceof Function) {
      if(source.prototype) { // 有 prototype 就是普通函数
        result = function(){ return source.apply(this, arguments) }
      } else {
        result = (...args) => { return source.call(undefined, ...args) }
      }
    } else if(source instanceof Array) {
      result = []
    } else if(source instanceof Date) {
      result = new Date(source - 0)
    } else if(source instanceof RegExp) {
      result = new RegExp(source.source, source.flags)
    } else {
      result = {}
    }
    cache.set(source, result)
    for(let key in source) { 
      if(source.hasOwnProperty(key)){
        result[key] = deepClone(source[key], cache) 
      }
    }
    return result
  } else {
    return source
  }
}

4. jQuery.extend() method

 可以使用$.extend进行深拷贝
$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝

let a = {
    a: 1,
    b: { d:8},
    c: [1, 2, 3]
};
let b = $.extend(true, {}, a);
console.log(a.b.d === b.b.d); // false

4.1 jQuery.extend source code

 jQuery.extend = jQuery.fn.extend = function() {
  var options,
    name,
    src,
    copy,
    copyIsArray,
    clone,
    target = arguments[0] || {},
    i = 1,
    length = arguments.length,
    deep = false;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;

    // Skip the boolean and the target
    target = arguments[i] || {};
    i++;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && !jQuery.isFunction(target)) {
    target = {};
  }

  // Extend jQuery itself if only one argument is passed
  if (i === length) {
    target = this;
    i--;
  }

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) != null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy) {
          continue;
        }

        // Recurse if we're merging plain objects or arrays
        if (
          deep &&
          copy &&
          (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))
        ) {
          if (copyIsArray) {
            copyIsArray = false;
            clone = src && Array.isArray(src) ? src : [];
          } else {
            clone = src && jQuery.isPlainObject(src) ? src : {};
          }

          // Never move original objects, clone them
          target[name] = jQuery.extend(deep, clone, copy);

          // Don't bring in undefined values
        } else if (copy !== undefined) {
          target[name] = copy;
        }
      }
    }
  }
  // Return the modified object
  return target;
};

沉静地闪光
11.2k 声望3.1k 粉丝

值得做的事情,值得现在就去做并一直坚持做;