第二章:深拷贝和浅拷贝-灵析社区

懒人学前端

一、知识点讲解

1、什么是数据类型?

数据分为基本数据类型 (String, Number, Boolean, Null, Undefined,Symbol) 和引用数据类型。

基本数据类型的特点:直接存储在栈 (stack) 中的数据

引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。

2、ES11新增了什么数据类型?

bigint!

bigint使用方式:

let max=Number.MAX_SAFE_INTEGER
console.log(BigInt(max))//9007199254740991n 
console.log(BigInt(max)+BigInt(1))//9007199254740992n 正确
console.log(BigInt(max)+BigInt(2))//9007199254740993n 正确
console.log(BigInt(max)+BigInt(4))//9007199254740995n 正确

3、为什么要使用深拷贝?

我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

4、赋值和浅拷贝的区别?

当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。

二、浅拷贝

浅拷贝是一点一点地拷贝一个对象。它将创建一个新对象。此对象具有原始对象属性值的精确副本。如果属性是基本类型,它将复制基本类型的值;如果属性是内存地址(引用类型),则复制内存地址。因此,如果一个对象更改地址,另一个对象将受到影响。也就是说,默认复制构造函数只复制浅层复制中的对象(依次复制成员),即只复制对象空间,而不复制资源。

        var obj1 ={
         name:'张三',
         age:8,
         pal:['王五','王六','王七']
        }
        var obj3 = shallowCopy(obj1)
        function shallowCopy (src){
         var newObj = {};
         for(var prop in src ){
             console.log(prop)
             if(src.hasOwnProperty(prop)){
                 newObj[prop] = src[prop]
             }
         }
         return newObj
        }
         obj3.name = '李四'
         obj3.pal[0] = '王麻子'
           
        console.log("obj1", obj1); //{age: 8, name: "张三", pal: ['王麻子', '王六', '王七']}
        console.log("obj3", obj3); //{age: 8, name: "李四", pal: ['王麻子', '王六', '王七']}

三、深拷贝

方法一:递归实现深拷贝

实现深拷贝原理的递归方法:遍历对象、数组甚至内部都是基本的数据类型,然后复制它们,即深度复制。

var obj = {   //原数据,包含字符串、对象、函数、数组等不同的类型
       name:"test",
       main:{
           a:1,
           b:2
       },
       fn:function(){
           
       },
        friends:[1,2,3,[22,33]]
   }
 
   function copy(obj){
        let newobj = null;   //声明一个变量用来储存拷贝之后的内容
        
     //判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可,
     //由于null不可以循环但类型又是object,所以这个需要对null进行判断
        if(typeof(obj) == 'object' && obj !== null){ 
        
        //声明一个变量用以储存拷贝出来的值,根据参数的具体数据类型声明不同的类型来储存
            newobj = obj instanceof Array? [] : {};   
            
        //循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
            for(var i in obj){  
                newobj[i] = copy(obj[i])
            }
        }else{
            newobj = obj
        }    
        console.log('77',newobj)
      return newobj;    //函数必须有返回值,否则结构为undefined
   }
 
    var obj2 = copy(obj)
    obj2.name = '修改成功'
    obj2.main.a = 100
   console.log(obj)
   console.log(obj2)

方法二:递归实现深拷贝

  • 递归处理 引用类型
  • 保持数组类型
function deepCopy(target) {
        if (typeof target === 'object') {
                const newTarget = Array.isArray(target) ? [] : Object.create(null)
                for (const key in target) {
                        newTarget[key] = deepCopy(target[key])
                }
                return newTarget
        } else {
                return target
        }
}

方法三:递归实现深拷贝

  • 哈希表 Map 支持 循环引用

Map 支持引用类型数据作为键

function deepCopy(target, h = new Map) {
        if (typeof target === 'object') {
                if (h.has(target)) return h.get(target)
                const newTarget = Array.isArray(target) ? [] : Object.create(null)
                for (const key in target) {
                        newTarget[key] = deepCopy(target[key], h)
                }
                h.set(target, newTarget)
                return newTarget
        } else {
                return target
        }
}

方法四:递归实现深拷贝

  • 哈希表 WeakMap 代替 Map
  • WeakMap 的键是弱引用,告诉 JS 垃圾回收机制,当键回收时,对应 WeakMap 也可以回收,更适合大量数据深拷
function deepCopy(target, h = new WeakMap) {
    if (typeof target === 'object') {
      if (h.has(target)) return h.get(target)
      const newTarget = Array.isArray(target) ? [] : Object.create(null)
      for (const key in target) {
        newTarget[key] = deepCopy(target[key], h)
      }
      h.set(target, newTarget)
      return newTarget
    } else {
      return target
    }
}


阅读量:2041

点赞量:0

收藏量:0