FE-Interview icon indicating copy to clipboard operation
FE-Interview copied to clipboard

什么是深拷贝,和浅拷贝有什么区别,动手实现一个深拷贝

Open Genzhen opened this issue 5 years ago • 5 comments

Genzhen avatar Jul 07 '20 01:07 Genzhen

扫描下方二维码,获取答案以及详细解析,同时可解锁800+道前端面试题。

Genzhen avatar Jul 07 '20 01:07 Genzhen

区别

  • 浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的属性值,因此修改拷贝后的属性值是引用类型的,就会影响源对象
  • 深拷贝就是对对象以及对象的所有子对象进行拷贝

实现

function cloneDeep(obj) {
    if(obj === null) return null;
    if (typeof obj !== 'object') return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    // 获取obj的构造函数并实例化一个新的
    const cloneObj = new obj.constructor();
    Object.keys(obj).forEach(key => {
        // 递归拷贝属性
        cloneObj[key] = cloneDeep(obj[key]);
    });
    return cloneObj;
}

let obj = {
    a: 100,
    b: [10, 20, 30],
    c: {
        x: 10
    },
    d: new Date(),
    f: undefined,
    g: /\s+/g,
    fn: function () { },
    symbol: Symbol.for('Symbol')
};

let arr = [10, [100, 200], {
    x: 10,
    y: 20
}];

let clone = JSON.parse(JSON.stringify(obj));
console.log(clone);
let clone1 = cloneDeep(obj);
console.log(clone1);

// { a: 100,
//     b: [ 10, 20, 30 ],
//     c: { x: 10 },
//     d: '2020-08-01T14:46:12.461Z',
//     g: {} }
//   { a: 100,
//     b: [ 10, 20, 30 ],
//     c: { x: 10 },
//     d: 2020-08-01T14:46:12.461Z,
//     f: undefined,
//     g: /\s+/g,
//     fn: [Function: fn],
//     symbol: Symbol(Symbol) }

GolderBrother avatar Aug 01 '20 14:08 GolderBrother

// 获取数据类型
function typeOf(any) {
  return Object.prototype.toString.call(any).split(' ')[1].slice(0, -1)
}

function deepClone(any) {
  // 避免循环引用
  const cache = new Map()

  // 根据不同类型的数据进行不同的拷贝方式,cache保存已复制属性引用,防止重复引用
  function recursionClone(any) {
    if (cache.get(any)) {
      return cache.get(any)
    }

    const type = typeOf(any)

    // 基本类型直接返回
    if (['String', 'Number', 'Null', 'Boolean', 'Undefined', 'Symbol'].includes(type)) {
      return any
    }

    if (type === 'RegExp') {
      return new RegExp(any)
    }

    if (type === 'Date') {
      return new Date(any)
    }

    if (type === 'Map') {
      const m = new Map()

      any.forEach((subValue, key) => {
        m.set(key, recursionClone(subValue))
      })

      return m
    }

    if (type === 'Set') {
      const m = new Set()

      any.forEach((subValue) => {
        m.add(recursionClone(subValue))
      })

      return m
    }

    // 函数类型使用 new Function 重新生成新的函数
    if (type === 'Function') {
      return new Function('return ' + any.toString())()
    }

    let o
    
    if (type === 'Array') {
      o = new Array()
    }

    if (type === 'Object') {
      o = new Object()
    }

    if (o) {
      // 引用类型则对其复制后对象进行缓存
      cache.set(any, o)
      copyOwnProperties(any, o)
      return o
    }

    return null
  }

  // 复制from的属性到to上
  function copyOwnProperties(from, to) {
    const names = Object.getOwnPropertyNames(from)

    // 遍历自身的可枚举属性(字符串属性和 Symbol属性)
    Reflect.ownKeys(from).forEach(key => {
      if (from.propertyIsEnumerable(key)) {
        to[key] = recursionClone(from[key])
      }
    })

    return to
  }

  return recursionClone(any)
}

const s = Symbol(1)

const test = {
  date: new Date('2022-08-12'),
  reg: /\s/g,
  func: function (name) {
    console.log(name)
  },
  obj: {
    a: 1,
    b: 2,
    c: 3
  },
  arr: [1, 2, 3],
  a: 1,
  b: null,
  c: undefined,
  d: '1',
  e: true,
  map: new Map([
    [1, 'aaa'],
    [2, 'bbb'],
    [3, 'ccc']
  ]),
  set: new Set([1, 2, 3]),
  symbol: Symbol('Symbol'),
  [s]: 2
}

// 循环引用
test.testObj = test

console.log(deepClone(test))

chinbor avatar Aug 12 '23 02:08 chinbor

/**
 * 什么是深拷贝,和浅拷贝有什么区别,动手实现一个深拷贝
 * js中基础类型的值直接存在栈内存中
 * 引用类型(对象/函数)存在堆内存中,栈内存中存储指向该堆的地址
 * 深拷贝指在堆内存中开辟新的空间,复制属性,栈内存中指针指向新的堆,拷贝对象与被拷贝对象是相互独立的,
 * 修改其中一个的属性值不会影响到另一个
 * 浅拷贝指仅仅拷贝地址指向,拷贝对象与被拷贝对象指向相同的堆,修改其中一个的属性时会影响到另一个
 */

function deepClone(obj) {
  if (typeof obj !== "object" || obj !== null) {
    return obj;
  }

  if (obj instanceof Array) {
    const newArr = [];
    obj.forEach((e) => {
      newArr.push(deepClone(e));
    });
    return newArr;
  }

  const newObj = {};
  for (const key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      const element = obj[key];
      newObj[key] = deepClone(element);
    }
  }
  return newObj;
}

const obj1 = {
  a: 1,
  b: {
    c: 2,
  },
  d: [{ e: { f: 3 } }],
};

const obj2 = deepClone(obj1);

obj1.a = 100;
obj1.b.c.g = [1, [2]];
delete obj1.d[0].e.f;

console.log(obj1);
console.log(obj2);

Kisthanny avatar Mar 21 '24 10:03 Kisthanny