【Js 浅拷贝与深拷贝】

Js 浅拷贝与深拷贝

Js 浅拷贝与深拷贝只针对像 Object 或 Array 这类引用数据类型,想了解Js的一些数据类型的同学,可详见我的另一篇文章 js 数据类型及类型判断,这里就不多详细说明。

浅拷贝:

像我们在对一些数据类型进行简单的赋值处理,然后用新的变量改变里面的属性的值后,会发现原本的数据也被改变了,如下示例:

let obj = {
    
    
 a: '12',
 b: 2
}
let simpleCopy = obj
simpleCopy.b = 5 // 或者执行 obj.b = 5 ,输出结果一致
console.log(obj) // {a: '12', b: 5}
console.log(simpleCopy) // {a: '12', b: 5}

这是因为引用数据类型的值是按引用访问的。obj.b和simpleCopy.b都是指向存的值的地址,而不是值本身。

深拷贝:

在我们处理数据时,有些时候肯定不想自己原来的数据被替换或者更改,深拷贝就是解决这方面的问题。下面我会列举一些常见的深拷贝的方式。

方式一:Object.assign

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象source复制到目标对象。它将返回目标对象target。但是我们需要注意的是,它只对最外层的进行深拷贝,也就是当对象内嵌套有对象的时候,被嵌套的对象进行的还是浅拷贝。

let obj = {
    
    
  a: '12',
  b: 2,
  c: {
    
     K1: NaN, K2: function(){
    
    } }
}
let deepCopy = Object.assign({
    
    }, obj)
deepCopy.b = 5
deepCopy.c.K1 = Infinity
console.log(obj)
console.log(deepCopy)

输出结果:

在这里插入图片描述

方式二:JSON.parse 和 JSON.stringify

这是一个常见的深拷贝实现的方式,此前我也常用这个方法进行一些数据处理。

let obj = {
    
    
  a: '12',
  b: 2,
  c: {
    
     K1: NaN, K2: function(){
    
    } }
}
let deepCopy = JSON.parse(JSON.stringify(obj))
deepCopy.b = 5
deepCopy.c.K1 = Infinity
console.log(obj)
console.log(deepCopy)

输出结果:

扫描二维码关注公众号,回复: 15494463 查看本文章

在这里插入图片描述

缺点: 这里我们看出来好像没啥问题,也成功拷贝一份数据,并且不影响原来的数据。但是JSON.stringify这个方式在数据转换的时候,一些由构造函数生成的数据会丢失对象的constructor。像下面情况:

let obj = {
    
    
 a: '12',
 b: 2,
 c: new Date(),
 d: NaN
}
let deepCopy = JSON.parse(JSON.stringify(obj))
deepCopy.b = 5
console.log(obj)
console.log(deepCopy)

输出结果:

在这里插入图片描述

像Date、Error、RegExp、function、NaN、Infinity、-Infinity 和 undefined等等数据使用 JSON.stringify 方法都会使原本的值丢失或是被替换。这样的话,在一些情境下使用这类方式就不太行。

方式三:deepCopy 递归赋值(推荐)

比较上面的几个深度拷贝的方式,都或多或少有些缺点,所以我们可以自己封装一个深度拷贝的方法。

function cloneDepth(initalObj, finalObj = {
     
     }) {
    
    
  let bol = isArrayOrObject(initalObj)
  if (!bol) return initalObj 
  Object.keys(initalObj).forEach((key) => {
    
    
    bol = isArrayOrObject(initalObj[key])
    finalObj[key] = bol ? cloneDepth(initalObj[key], bol === 'Array' ? [] : {
    
    }) : initalObj[key]
  })
  // // 或者采用 for in
  // for (var key in initalObj) {
    
    
  //   bol = isArrayOrObject(initalObj[key])
  //   finalObj[key] = bol ? cloneDepth(initalObj[key], bol === 'Array' ? [] : {}) : initalObj[key]
  // }
  return finalObj
}
function isArrayOrObject(list) {
    
    
  let isBool = false
  switch (outputDataType(list)) {
    
    
    case 'Object': isBool = 'Object'; break;
    case 'Array': isBool = 'Array'; break;
    default: isBool = false; break;
  }
  return isBool
}
function outputDataType(data) {
    
    
  let dataTypeStr = Object.prototype.toString.call(data)
  dataTypeStr = dataTypeStr.match(/\[object (\S*)\]/)[1]
  return dataTypeStr
}

调用我们封装的方法

let obj = {
    
    
  a: '12',
  b: 2,
  c: {
    
     k1: '1', k2: 23, k3: true },
  d: ['11', 2, null, undefined, new Date()],
  e: function () {
    
     },
  f: null,
  h: new Date(),
  i: Infinity,
  j: new RegExp('12'),
  n: NaN
}
let deepCopy = cloneDepth(obj)
console.log(obj)
console.log(deepCopy)

输出结果:

在这里插入图片描述

跟原本的数据一模一样,这样封装的cloneDepth方法就可以好好使用了。

猜你喜欢

转载自blog.csdn.net/weixin_42927679/article/details/125782617