JavaScript 深浅拷贝以及特殊类型拷贝问题

1. 数组

  • 当数组只有一个层级时

    • rest运算符,如下代码
    const arr = [1, 2, 3, 4, 5];
    const arr2 = [...arr];
    console.log(arr === arr2);
    //false
    arr[0] = 111;
    console.log(arr[0], arr);
    // 111 [111, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • slice方法,如下代码
    const arr = [1, 2, 3, 4, 5];
    const arr2 = arr.slice();
    console.log(arr === arr2);
    //false
    arr[0] = 222;
    console.log(arr[0], arr);
    // 222 [222, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • concat方法,如下代码
    const arr = [1, 2, 3, 4, 5];
    const arr2 = arr.concat();
    console.log(arr === arr2);
    //false
    arr[0] = 333;
    console.log(arr[0], arr);
    // 333 [333, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • for循环,如下代码
    const arr = [1, 2, 3, 4, 5];
    let arr2 = [];
    for (let i = 0; i < arr.length; i++) {
      arr2[i] = arr[i];
    }
    console.log(arr2 === arr);
    // false
    arr[0] = 123;
    console.log(arr[0], arr);
    //123 [123, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • for of循环,如下代码
    const arr = [1, 2, 3, 4, 5];
    let arr2 = [];
    for (let item of arr) {
      arr2.push(item);
    }
    console.log(arr2 === arr);
    // false
    arr[0] = 123;
    console.log(arr[0], arr);
    //123 [123, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • for in循环,如下代码
    const arr = [1, 2, 3, 4, 5];
    let arr2 = [];
    for (let idx in arr) {
      arr2.push(arr[idx]);
    }
    console.log(arr2 === arr);
    // false
    arr[0] = 123;
    console.log(arr[0], arr);
    //123 [123, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    
    • JSON.parse以及JSON.stringify,如下代码
    const arr = [1, 2, 3, 4, 5];
    let arr2 = JSON.parse(JSON.stringify(arr));
    console.log(arr2 === arr);
    // false
    arr[0] = 123;
    console.log(arr[0], arr);
    //123 [123, 2, 3, 4, 5]
    console.log(arr2[0], arr2);
    // 1 [1, 2, 3, 4, 5]
    

    总结:当数组中的item为复杂类型时,以上方法为浅拷贝

2. 对象

  • 当对象里面的每项都为简单类型时

    • Object.assign,当对象里面每项都为简单类型时,是深拷贝,当存在复杂类型时,就是浅拷贝。
    const obj = {
      a: 1,
      b: true,
      c: null,
      d: NaN,
      e: undefined,
      f: function () {},
      g: 'string',
    };
    
    const obj2 = Object.assign({}, obj);
    
    console.log(obj === obj2);
    // false
    obj.a = 2;
    console.log(obj);
    // {a: 2, b: true, c: null, d: NaN, e: undefined, …}
    console.log(obj2);
    // {a: 1, b: true, c: null, d: NaN, e: undefined, …}
    

    总结:同样对于for in方法遍历,rest运算符,JSON.parse以及JSON.stringify,需要判断对象中的数据类型,看是深拷贝,还是浅拷贝

3. 特殊类型的拷贝问题

  • 使用JSON.parse(JSON.stringify())拷贝数组

    const arr = [undefined, function () {}, () => {}, NaN];
    let arr2 = JSON.parse(JSON.stringify(arr));
    console.log(arr);
    // [undefined, ƒ, ƒ, NaN]
    console.log(arr2);
    // [null, null, null, null]
    

    当数据类型为undefined,function,arrow function,NaN时,使用JSON.parse(JSON.stringify())后,数据会转换成null

  • 使用JSON.parse(JSON.stringify())拷贝对象

    const obj = {
      a: undefined,
      b: function () {},
      c: () => {},
      d: NaN,
    };
    
    const obj2 = JSON.parse(JSON.stringify(obj));
    
    console.log(obj);
    // {a: undefined, d: NaN, b: ƒ, c: ƒ}
    console.log(obj2);
    // {d: null}
    

    当对象类型中存在undefined,function,arrow function,NaN时,使用JSON.parse(JSON.stringify())后,undefined,function,arrow function,会直接丢失,NaN会被转换为null

  • 当存在类型为Date,Error,RegExp,时

    const obj = {
      a: new Date(),
      b: new Error(),
      c: /\d+/,
    };
    
    const obj2 = JSON.parse(JSON.stringify(obj));
    
    console.log(obj);
    // {a: undefined, d: NaN, b: ƒ, c: ƒ}
    console.log(obj2);
    // {d: null}
    

    image
    当对象类型中存在Date,Error,RegExp时,使用JSON.parse(JSON.stringify())后,Error,RegExp会被转换为{},Date格式会被转换

4. 深拷贝,考虑所有可能的类型

  • typescript
const obj = {
  a: 1,
  b: '2',
  c: null,
  d: undefined,
  e: NaN,
  f: function () {},
  g: () => {},
  h: { h1: '' },
  i: [1, 2, 3, 4],
  j: /\d+/gi,
  k: new Error('error'),
  l: new Map(),
  m: new Set(),
  n: new Date(),
  o: true,
};

function deepCopy<T extends Array<T> | { [key: string]: any }>(data: T): T {
  if (Array.isArray(data)) {
    return data.map((item) => deepCopy(item)) as T;
  }
  const result: T = {} as T;
  const getType = (_v: any) => Object.prototype.toString.call(_v);
  if (getType(data) === '[object Object]') {
    return Object.keys(data).reduce((result, key) => {
      const _type = getType(data[key]);
      switch (_type) {
        case '[object RegExp]':
        case '[object Date]':
        case '[object Error]':
          result[key] = data[key].constructor(data[key]);
          break;
        case '[object Object]':
        case '[object Array]':
          result[key] = deepCopy(data[key]);
          break;
        default:
          result[key] = data[key];
      }
      return result;
    }, result);
  }
  return data;
}
  • javascript
function deepCopy(data) {
    if (Array.isArray(data)) {
        return data.map((item) => deepCopy(item));
    }
    const result = {};
    const getType = (_v) => Object.prototype.toString.call(_v);
    if (getType(data) === '[object Object]') {
        return Object.keys(data).reduce((result, key) => {
            const _type = getType(data[key]);
            switch (_type) {
                case '[object RegExp]':
                case '[object Date]':
                case '[object Error]':
                    result[key] = data[key].constructor(data[key]);
                    break;
                case '[object Object]':
                case '[object Array]':
                    result[key] = deepCopy(data[key]);
                    break;
                default:
                    result[key] = data[key];
            }
            return result;
        }, result);
    }
    return data;
}
posted @ 2019-11-02 11:16  半糖也甜吖  阅读(196)  评论(0编辑  收藏  举报