javascript深浅拷贝

215 阅读3分钟

前言

什么是拷贝?指的是js变量赋值

如何区分深浅拷贝?将变量a的值赋给变量b,修改其中变量a,变量b受影响(浅拷贝),不受影响(深拷贝)

要想理解深浅拷贝,首先得js的数据类型入手,因为在js中基本类型和引用类型的值存储方式不同,赋值处理不用

基本类型

Boolean
Number//有几个特殊值包括NaN(意 指“not a number”, 更确切地说是“invalid number”)、 +Infinity、 -Infinity 和 -0
String
Null
undefined
Symbol(ES6+)
BigInt(ES11+)

基本类型是不可变的,不可以给基本类型添加属性和方法(添加不报错,但是无效)

引用类型

Object	// 包含Array、FunctionDate、RegExp、Error

引用类型是可变的,可以给基本类型添加属性和方法,也可以删除属性和方法

赋值原理

let num1=10
let obj1={name:'obj1'}

let num2=num1
let obj2=obj1

num2=20
obj2.name='obj2'

console.log(num1)//10
console.log(obj1.name)//obj2

之所以会出现深浅拷贝,是由于 JS 对基本类型和引用类型的处理不同

  1. 基本类型指的是简单的数据段

  2. 引用类型指的是一个对象保存在堆内存中的地址

  3. JS 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。

总结:在复制时也是一样,如果我们复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。而如果我们复制一个引用类型时,同样会把变量中的值复制一份放到新的变量空间里,但此时复制的东西并不是对象本身,而是指向该对象的指针。所以我们复制引用类型后,两个变量其实指向同一个对象,所以改变其中的一个对象,会影响到另外一个。

浅拷贝

复制第一层属性的值或者内存地址

  1. lodash提供的_.clone()
  2. Object.assign()Object.create()
  3. {...obj}
  4. 手写浅拷贝
function shallowClone(sourceObj={}){
	let targetObj=Array.isArray(sourceObj)?[]:{};
    let copy;
   	for(var key in sourceObj){
    	copy=sourceObj[key];
        targetObj[key]=copy;
    }
    return targetObj
}

深拷贝

完全拷贝,生成一份值相同但是不同内存地址的对象

  1. lodash提供的_.cloneDeep()
  2. JSON.parse(JSON.stringify()),但是有很多副作用
  1. 值为undefined,会丢失这个属性,
  2. 值为函数,会丢失这个属性,
  3. 值为Date类型,会调用toJSON(),
  4. 值为RegExp类型,会变成{},
  5. 循环引用的话,会报错
  1. 手写深拷贝
function deepClone(obj, cache = new WeakMap()) {
  if (!obj instanceof Object) return obj
  // 防止循环引用
  if (cache.get(obj)) return cache.get(obj)
  // 支持函数
  if (obj instanceof Function) {
    return function () {
      return obj.apply(this, arguments)
    }
  }
  // 支持日期
  if (obj instanceof Date) return new Date(obj)
  // 支持正则对象
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)

  // 数组是 key 为数字索引的特殊对象
  const res = Array.isArray(obj) ? [] : {}
  // 缓存 copy 的对象,用于处理循环引用的情况
  cache.set(obj, res)

  Object.keys(obj).forEach((key) => {
    if (obj[key] instanceof Object) {
      res[key] = deepClone(obj[key], cache)
    } else {
      res[key] = obj[key]
    }
  });
  return res
}

参考

segmentfault.com/a/119000001…