一文了解JS深浅拷贝

36 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

在项目开发中,深浅拷贝的使用是非常常见的场景,拷贝之所以有深浅,是因为JS 数据类型之间的差异,导致存放的位置不同(栈和堆)

JS 的数据类型分为:

  • 基本数据类型
    • 基本数据类型存放在
    • 存储基本数据类型及引用类型数据的堆地址
  • 引用数据类型
    • 引用数据类型存放在
    • 存储引用类型数据

image.png

1. 浅拷贝

浅拷贝是拷贝一层,深层次的引用类型则共享内存地址。新对象与旧对象共享同一块内存,修改对象属性会影响原对象

可实现浅拷贝的方法:

  • Object.assign
    • const newObj = Object.assign({}, obj);
  • Array.prototype.slice(), Array.prototype.concat()
    • const arr1 = arr.slice(0)
    • const arr2 = arr.concat()
  • 使用...(拓展运算符)实现

代码实现

  • obj.hasOwnProperty() 是 Object 的原型方法,检测属性是否为对象的自有属性
  • obj.hasOwnProperty('c1') // false, obj对象没有c1属性 (oldData中的a、b、c都可以浅拷贝,c1不能)
function shallowClone(obj) {
    const newObj = {};
    for(let item in obj) {
        if(obj.hasOwnProperty(item)){ 
            newObj[item] = obj[item];  
        }
    }
    return newObj;
}
const oldData = {
    a:1,
    b:2,
    c: {
        c1:3
    }
}
const newData = shallowClone(oldData)
newData.a = 10
newData.c.c1 = 200
console.log(`oldData.a: ${oldData.a}`)
console.log(`newData.a: ${newData.a}`)
console.log(`oldData.c.c1: ${oldData.c.c1}`)
console.log(`newData.c.c1: ${newData.c.c1}`)

运行结果:

image.png

2. 深拷贝

深拷贝时,新生成一个站栈,新对象与原对象不共享内存,修改新对象的值不会影响原对象

可实现深拷贝的方法:

  • _.cloneDeep()
    • const _ = require('lodash'); const obj1 = _.cloneDeep(obj);
  • jQuery.extend()
  • JSON.stringify()
    • 弊端:会忽略 undefined、symbol 和函数,当数据为函数的时候,拷贝的结果为null;当数据为正则的时候,拷贝结果为一个空对象{}
  • 手写一个深拷贝函数

代码实现

手写一个深拷贝函数,可以解决其它方式拷贝不了函数以及 undefined 的问题

注: 普通的递归无法解决循环引用问题,所以使用 WeakMap:

WeakMap

  • WeakMap 仅接受对象(Map 不限制)
  • WeakMap是一种弱引用,所以不会造成内存泄漏(下一次垃圾回收机制执行时,内存会被释放掉)
  • 方法:delete、get、has、set
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 当传入的参数是null或者undefined时,null==undefined 直接返回
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (typeof obj !== "object") return obj;
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor(); // 深拷贝
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);// 递归
    }
  }
  return cloneObj;
}
const oldData = {
  a:1,b:2,
  c: {
    c1:3
  }
}
let newData = deepClone(oldData)
newData.a = 10
newData.c.c1 = 200
console.log(`oldData.a: ${oldData.a}`)
console.log(`newData.a: ${newData.a}`)
console.log(`oldData.c.c1: ${oldData.c.c1}`)
console.log(`newData.c.c1: ${newData.c.c1}`)

运行结果:

image.png