javascript中的深拷贝(代码片段)

LiuJun2Son LiuJun2Son     2023-03-24     163

关键词:

1.什么是深拷贝

浅拷贝只是解决了第一层的拷贝问题,拷贝第一层的***基本类型值***,以及第一层的***引用类型地址***,并没有递归拷贝第二层以后的属性。

深拷贝会拷贝所有的属性,拷贝的属性指向动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。深拷贝可以解决多层拷贝的问题。

2.深拷贝的使用

1.JSON.parse(JSON.stringify(object))

案例1:根据原始A对象浅拷贝一个新的B对象出来

// 1.JSON.parse(JSON.stringify(object)) 深拷贝
  let A = 
    name:'liujun',
    age:25,
    job:
      name:'web',
      year:4
    ,
    lan:[
      'java',
      'js'
    ]
  
  console.log(A) //  name: 'liujun', age: 25, job:  name: 'web', year: 4 , lan: [ 'java', 'js' ]  
  // 1.1 根据原始A对象深拷贝一个新的B对象出来
  let B = JSON.parse(JSON.stringify(A))
  console.log(B) //  name: 'liujun', age: 25, job:  name: 'web', year: 4 , lan: [ 'java', 'js' ] 

  // 1.2 修改A对象3个属性。发现B对象并没有影响。拷贝前后两个对象互不影响。
  A.name = '刘军'
  A.job.name = '前端开发'
  A.lan[1] = 'JavaScript'
  console.log('--------------------------------')
  console.log(A) //  name: '刘军', age: 25, job:  name: '前端开发', year: 4 , lan: [ 'java', 'JavaScript' ] 
  console.log(B) //  name: 'liujun', age: 25, job:  name: 'web', year: 4 , lan: [ 'java', 'js' ] 

总结:JSON.parse(JSON.stringify(object)) 深拷贝会拷贝所有的属性,拷贝的属性指向动态分配的内存。拷贝前后两个对象互不影响。

案例2:根据原始 A浅拷贝一个新的B对象出来

  // 1.JSON.parse(JSON.stringify(object)) 深拷贝
  let A = 
    name:'liujun', // 1.拷贝成功
    value: undefined, // 3.没有拷贝
    default: null, // 1.拷贝成功
    symbol:Symbol('liujun'), // 3.没有拷贝
    show:function()  // 3.没有拷贝
      console.log('show')
    ,
    date:new Date(), // 2.拷贝失败,变成了字符串
    reg:new RegExp(/1[3|4|5|6|7|8]\\d9/)  // 2.拷贝失败,变成了
  
  
  console.log(A) 
  /**
     
      name: 'liujun',
      value: undefined,
      default: null,
      symbol: Symbol(liujun),
      show: [Function: show],
      date: 2019-09-29T01:32:32.911Z,
      reg: /1[3|4|5|6|7|8]\\d9/ 
   
   */
  // 1.1 根据原始A对象深拷贝一个新的B对象出来
  let B = JSON.parse(JSON.stringify(A))
  console.log(B) 
  /**
   
    name: 'liujun',
    default: null,
    date: '2019-09-29T01:32:32.911Z',
    reg:  
  
   */

  console.log(typeof A.date) // object:还是Date的对象
  console.log(typeof B.date) // string:不是Date的对象,不能调用Date对象的方法
  

总结:JSON.parse(JSON.stringify(object)) 深拷贝会拷贝部分的属性,拷贝的属性指向动态分配的内存。拷贝前后两个对象互不影响。

存在的问题:

  1. Date 和 RegExp类型虽然拷贝了,但是拷贝还是失败,类型发生了改变。Date 类型变成了字符串,RegExp变成了, 在使用它拷贝的时候一定要注意

  2. undefined、null、Symbol、function压根就没有拷贝, 在使用它拷贝的时候一定要注意。

  3. 对象的循环引用时的拷贝会报错, 例如下面 c 指向了 b 的引用,属于循环引用:

    let obj = 
        a: 1,
        b: 
            c: 2,
       		d: 3
        
    
    obj.a = obj.b;
    obj.b.c = obj.a;
    
    let b = JSON.parse(JSON.stringify(obj));
    // Uncaught TypeError: Converting circular structure to JSON
    

2.编写深拷贝函数

1.编写浅拷贝

function deepCopy(object)
    var target = 
    for(var key in object)
      // 1.判断是否是自身的属性。object借用hasOwnProperty(key)函数,传递key参数
      if(Object.prototype.hasOwnProperty.call(object, key))
          target[key] = object[key]
      
    
    return target

2.编写深拷贝函数

function deepCopy(object)
    var target = 
    for(var key in object)
      // 1.判断是否是自身的属性。object借用hasOwnProperty(key)函数,传递key参数
      if(Object.prototype.hasOwnProperty.call(object, key))
        // 2.如果object[key]是对象或者是数组需要递归遍历
        if(typeof object[key] === 'object')
          target[key] = deepCopy(object[key]) // 3.深拷贝比浅拷贝就是多了这里
         else 
          target[key] = object[key]
        
       
      
    
    return target

3.优化深拷贝函数

function deepCopy(object)
  // typeof null // "object"
  // typeof  // "object"
  // typeof [] // "object"
  // typeof new Date() // "object"
  // typeof new RegExp() // "object"
  // typeof function foo() // "function"
  if(typeof object === 'object' && object !=null)
      
    var target = Array.isArray(object) ? [] : 
    for(var key in object)
      // 1.判断是否是自身的属性。object借用hasOwnProperty(key)函数,传递key参数
      if(Object.prototype.hasOwnProperty.call(object, key))
        // 2.如果object[key]是对象或者是数组需要递归遍历,例如:, [], new Date(), new RegExp() 
        if(typeof object[key] === 'object')
          target[key] = deepCopy(object[key]) // 3.深拷贝比浅拷贝就是多了这里
         else 
          target[key] = object[key]
        
       
      
    
    return target
      
  else 
    // 传入 `undefined、null、Symbol、function` ` 时应返回 `undefined、null、Symbol、function` `  
    return object
  


1.对传入参数进行校验,传入 null 时应该返回 null 而不是 , 例如:

if(typeof object === 'object' && object !=null)

2.考虑数组的兼容,例如:

var target = Array.isArray(object) ? [] :

3.测试深拷贝函数

  // 1.deepCopy(object) 深拷贝
  let A = 
    name:'liujun', // 1.拷贝成功
    value: undefined, // 1.拷贝成功
    default: null, // 1.拷贝成功
    symbol:Symbol('liujun'), // 1.拷贝成功
    show:function()  // 1.拷贝成功
      console.log('show')
    ,
    date:new Date(), // 2.拷贝失败,变成了
    reg:new RegExp(/1[3|4|5|6|7|8]\\d9/)  // 2.拷贝失败,变成了
  
  console.log(A) 
  /**
     
      name: 'liujun',
      value: undefined,
      default: null,
      symbol: Symbol(liujun),
      show: [Function: show],
      date: 2019-09-29T01:32:32.911Z,
      reg: /1[3|4|5|6|7|8]\\d9/ 
   
   */
  // 1.1 根据原始A对象深拷贝一个新的B对象出来
  let B = deepCopy(A)
  console.log(B) 
  /**
   
    name: 'liujun',
    value: undefined,
    default: null,
    symbol: Symbol(liujun),
    show: [Function: show],
    date: ,
    reg:  
  
   */

  console.log(typeof A.date) // object:还是Date的对象
  console.log(typeof B.date) // object:不是Date的对象,不能调用Date对象的方法

存在的问题:

  1. Date 和 RegExp` 类型虽然拷贝了,但是拷贝还是失败,类型发生了改变。Date 类型变成了,RegExp变成了。为什么会是 ?是递归函数返回的
  2. undefined、null、Symbol、function 属于直接拷贝, 直接走了 deepCopy 的 else 代码块返回原始数据。
  3. 对象的循环引用时的拷贝会报错

3.完整深度拷贝函数

// 1.获取到数据源的数据类型
function _getDataType(data)    
  return Object.prototype.toString.call(data).slice(8, -1)

// 2.负责拷贝 RegExp 对象
function copyRegExp(regExp) 
  let attrs = ''
  if (regExp.global) attrs += 'g'
  if (regExp.ignoreCase) attrs += 'i'
  if (regExp.multiline) attrs += 'm'
  const newRegExp = new RegExp(regExp, attrs)
  newRegExp.lastIndex = regExp.lastIndex
  return newRegExp


// 3.深克隆,考虑到(1.类型检查,2.递归爆栈,3.相同引用,4.Date和Function等特殊类型克隆,原型克隆)
function deepClone(x) 
  // 3.1 String Number Boolean Undefined Null 返回自身
  if (x == null || typeof x !== 'object') return x
  // 3.2 RegExp Date Function 克隆
  const type = _getDataType(x)
  let root
  switch (type) 
    case 'RegExp':
      // 3.3克隆正则    
      return copyRegExp(x)
    case 'Date':
      // 3.4 克隆Date类型    
      return new Date(x.getTime())
    case 'Function':
      // 3.5 克隆函数类型    
      return x
    case 'Array':
      root = []
      break
    default:
      root = Object.create(Object.getPrototypeOf(x))
  
  // 3.6 Array Object 类型的克隆
  // 用来去重 解决原数据中多个属性引用同一对象克隆后不相同问题( 防止循环引用问题 )
  const uniqueList = []
  // 使用栈结构解决递归爆栈问题
  const stack = [
    
      parent: root,
      key: undefined,
      data: x
    
  ]
  // 深度优先循环(防止递归爆栈)
  while (stack.length) 
    const  parent, key, data  = stack.pop()
    // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
    let res = parent
    if (typeof key !== 'undefined') 
      const type = _getDataType(data)
      switch (type) 
        case 'RegExp':
          parent[key] = copyRegExp(data)
          continue
        case 'Date':
          parent[key] = new Date(data.getTime())
          continue
        case 'Function':
          parent[key] = data
          continue
        case 'Array':
          res = parent[key] = []
          break
        default:
          const proto = Object.getPrototypeOf(data)
          res = parent[key] = Object.create(proto)
      
    
    // 数据引用已经存在则赋值并退出本次循环,不存在则缓存
    const uniqueData = uniqueList.find(item => item.source === data)
    if (uniqueData) 
      parent[key] = uniqueData.target
      continue
     else 
      uniqueList.push(
        source: data,
        target: res
      )
    
    for (const k in data) 
      if (data.hasOwnProperty(k)) 
        if (data[k] == null || typeof data[k] !== 'object') 
          // 基础类型克隆
          const descriptor = Object.getOwnPropertyDescriptor(data, k)
          Object.defineProperty(res, k, descriptor)
         else 
          // 引用类型加入stack循环处理
          stack.push(
            parent: res,
            key: k,
            data: data[k]
          )
        
      
    
  
  return root

4.其它深拷贝函数

1.jQuery.extend()

案例1:根据原始 A浅拷贝一个新的B对象出来

var A = [1,2,3,4];
var B = $.extend(true,[],A);

2. lodash.cloneDeep()

案例1:根据原始 A浅拷贝一个新的B对象出来

var A = [ 'a': 1 ,  'b': 2 ];
var B = lodash.cloneDeep(A); //  _.cloneDeep()

javascript中的深拷贝(代码片段)

1.什么是深拷贝浅拷贝只是解决了第一层的拷贝问题,拷贝第一层的***基本类型值***,以及第一层的***引用类型地址***,并没有递归拷贝第二层以后的属性。深拷贝会拷贝所有的属性,拷贝的属性指向动态分配的... 查看详情

javascript中的深拷贝和浅拷贝

文章目录JavaScript中的变量类型深拷贝和浅拷贝的理解深拷贝和浅拷贝的实现方式为什么需要深拷贝和浅拷贝 JavaScript中的变量类型(1)、基本类型  JavaScript中的基本类型有五种:null、undefined、boolean、string、number。变量是按... 查看详情

java中的深拷贝(代码片段)

对象拷贝有时让我们忽视其重要性,又或者因为想当然而导致若干程序问题。浅拷贝浅拷贝即普通拷贝,即对要拷贝的对象进行复制。例如对于Entity类:classEntityinta;Stringb;List<String>c;List<Object>d;对于Entity类的对象entity,... 查看详情

python中的深拷贝和浅拷贝区别(代码片段)

首先,我们知道Python3中,有6个标准的数据类型,他们又分为可变和不可变。不可变:Number(数字)、String(字符串)、Tuple(元组)。可以变:List(列表)、Dictionary(字典)、Set(集合)。浅拷贝copy模块里面的copy方法实现。... 查看详情

javascript数组以及对象的深拷贝(复制数组或复制对象)的方法(代码片段)

javascript数组以及对象的深拷贝(复制数组或复制对象)的方法前言在js中,数组和对象的复制如果使用=号来进行复制,那只是浅拷贝。如下图演示:如上,arr的修改,会影响arr2的值,这显然在... 查看详情

c++中的深拷贝和浅拷贝构造函数(代码片段)

1,对象的构造在实际工程开发当中是相当重要的,C++中使用类就要创建对象,这就涉及了对象的构造,本节课讲解对象的构造和内存操作方面的问题;  2,实际工程开发中,bug产生的根源,必然的会有内存操作的问题,... 查看详情

解析js中的深拷贝和浅拷贝(代码片段)

...3:数组深拷贝的实现1.使用for循环 <scripttype="text/javascript"> vararr1=['a','b','c']; vararr2=[]; functiondeepCopy(arr1,arr2) for(vari=0;i<arr1.length;i++) arr2[i]=arr1[i]; deepCopy(arr1,arr2); arr2[1]... 查看详情

解析js中的深拷贝和浅拷贝(代码片段)

...3:数组深拷贝的实现1.使用for循环 <scripttype="text/javascript"> vararr1=['a','b','c']; vararr2=[]; functiondeepCopy(arr1,arr2) for(vari=0;i<arr1.length;i++) arr2[i]=arr1[i]; deepCopy(arr1,arr2); arr2[1]... 查看详情

java中的深拷贝和浅拷贝(代码片段)

目录🍎引出拷贝🍎浅拷贝🍎深拷贝🍎总结引出拷贝现在有一个学生类和书包类,在学生类中有引用类型的书包变量:classSchoolBagprivateStringbrand;//书包的品牌privateintsize;//书包的尺寸//getter、setter略publicSchoo... 查看详情

java对象的深拷贝(代码片段)

...贝可能依赖于原始对象。在深度复制方法中,我们确保树中的所有对象都被深度复制,因此副本不依赖于任何可能会更改的先前存在的对象。Maven设置我们将使用三个Maven依赖项Gson、Jackson和ApacheCommonsLang来测试深拷贝的不同方式... 查看详情

javascript实现引用数据类型的深拷贝和浅拷贝详解(代码片段)

关于引用类型值的详解,请看另一篇随笔 https://www.cnblogs.com/jinbang/p/10346584.html 深拷贝和浅拷贝,也就是引用数据类型栈和堆的知识点。深浅拷贝的原型都是Object,深拷贝指向的栈内存不一样,浅拷贝指向的栈内存一样)... 查看详情

数组和对象的深拷贝(代码片段)

...而不占用新的内存,所有一旦改变,就是直接改变的内存中的值,所以原来的值也改变了。深拷贝就是,启用新的内存,所以修改新的值,将不影响旧的值。 会产生新内存的一 查看详情

数组的深拷贝(代码片段)

slice方法实现数组的深拷贝这个代码实现非常简单。原理也比较好理解,他是将原数组中抽离部分出来形成一个新数组。我们只要设置为抽离全部,即可完成数组的深拷贝。代码如下:vararr=[1,2,3,4,5]vararr2=arr.slice(0)arr[2]=5console.log(... 查看详情

c++中的深拷贝和浅拷贝的案例对比理解(代码片段)

用两个案例对比来理解为什么要深拷贝和浅拷贝案例一#include<iostream>usingnamespacestd;/*简单说浅拷贝就是赋值操作:深拷贝就是在堆区间又申请了一个空间,进行拷贝操作*/classPerpublic: int*name;public: Per(intn) name=newint(n... 查看详情

javascript数组以及对象的深拷贝(复制数组或复制对象)的方法

javascript数组以及对象的深拷贝(复制数组或复制对象)的方法前言在js中,数组和对象的复制假设使用=号来进行复制。那仅仅是浅拷贝。例如以下图演示:如上。arr的改动,会影响arr2的值,这显然在绝大多数情况下,并非我们... 查看详情

清晰python的深拷贝和浅拷贝(代码片段)

强推可可老师视频pythontutor工具网址查了好多资料,看了好多示例代码,都没理解成功,b站上发现宝藏视频,分享给大家。文章目录1、可变对象(1)浅拷贝(2)深拷贝2、不可变对象1、可变对象可变对象包括list(... 查看详情

javascript浅拷贝与深拷贝(代码片段)

浅拷贝与深拷贝1. 什么是浅拷贝,深拷贝?2. 常用的浅拷贝方法2.1)'='赋值2.2)扩展运算符2.3)Object.assign()2.4)for...in3. 常用的深拷贝方法3.1)递归3.2)JSON做字符串转换1. 什么是浅拷贝,深拷贝?浅拷... 查看详情

js中如何进行对象的深拷贝(代码片段)

...组的引用,并没有真正地拷贝一个对象,那如何进行对象的深度拷贝呢?如果你对此也有疑问,这篇文章或许能够帮助到你一、对象引用、浅层拷贝与深层拷贝的区别js的对象引用传递理解起来很简单,参考如下代码:vara=name:‘... 查看详情