JavaScript深拷贝和浅拷贝 及 JSON.parse(JSON.stringify()) 的缺陷

目录

一、理解拷贝

二、基本数据类型拷贝

三、浅拷贝(shallow  clone)

对象浅拷贝

(1)依次赋值

(2)for..in 遍历

(3)Object.assign(目标对象,要拷贝的对象)

(4)展开运算符

缺陷:只能拷贝外层不能拷贝内层

 数组浅拷贝

(1)依次赋值

(2)展开运算符

(3)slice

(4)map

四、深拷贝

对象数组深拷贝

(1)递归

(2)JSON.parse(JSON.stringify())


一、理解拷贝

拷贝就像我们平常cv一些文件、代码 复制了一份新的就叫拷贝。 换成js代码就变成了基本数据类型和引用数据类型的拷贝。

在JavaScript中我们定义的简单数据类型会存到栈(stack)内存中,包括引用类型的指针,而这个指针指向的地方是在堆(heap)内存中。也通常理解为,简单数据类型存在栈中,引用数据类型存在堆中

二、基本数据类型拷贝

基本数据类型没什么好说的,通过一个变量赋值给一个变量就已经拷贝完成了

        let a = 1
        let b = a

 把a赋值给b之后 再去操作a并不会影响b   你可以理解为 a 和b 是两个不同的区域,但都是存在栈内存里。

        let a = 1
        let b = a
        a=5
        console.log(b); //1
        console.log(a); //5

三、浅拷贝(shallow  clone)

浅拷贝只能拷贝复杂数据类型的指针,并不能改变复杂数据类型的地址,只能拷贝外层,并不能彻底拷贝,例如数组中还有数组(对象),(准确来说是外层引用数据类型)

对象浅拷贝

(1)依次赋值

优点:数量较少的时候使用方便、简单,缺点:遇到对象或数组键比较多时,操作不方便

        let obj = {
            a: {name: 'abc'},
            b: 2
        }
        let newObj = {}
        newObj.a=obj.a
        newObj.b=obj.b

(2)for..in 遍历

        let obj = {
            a: {name: 'abc'},
            b: 2
        }
       let newObj = {} 
       for (const key in obj) {
          newObj[key]=obj[key]
       }

(3)Object.assign(目标对象,要拷贝的对象)

        let obj = {
            a:{name:'奥特曼'},
            b:2
        }
        let newObj= {}
        Object.assign(newObj,obj)

(4)展开运算符

         let obj = {
            a: {name: 'abc'},
            b: 2
        }
        let newObj = {}
        newObj = {...obj}

缺陷:只能拷贝外层不能拷贝内层

浅拷贝只能拷贝最外层的引用地址,并不能拷贝内层的引用地址  

修改最内层的数据 另一个也会改变,因为指针指向的的都是同一个内存地址

        newObj.a.name='bcd'
        console.log(obj);    //a: {name: "bcd"}, b: 2
        console.log(newObj); //a: {name: "bcd"}, b: 2

 数组浅拷贝

(1)依次赋值

        let arr  =  [1,{name:'abc'}]
        let newArr = []
        newArr[0]=arr[0] 
        newArr[1]=arr[1] 

(2)展开运算符

        let arr  =  [1,{name:'abc'}]
        let newArr = [...arr]

(3)slice

       let arr  =  [1,{name:'abc'}]
        let newArr = arr.slice(0)

(4)map

        let arr  =  [1,{name:'abc'}]
        let newArr = arr.map(it=>it)

同样修改数组中内存的数据 另一个数组的内存数据也会改变

        arr[1].name='bcd' 
        console.log(arr);// 1, {name: "bcd"}
        console.log(newArr);// 1, {name: "bcd"}

四、深拷贝

深拷贝:不光外层的引用地址该变了 内层的引用数据类型也发生改变

对象数组深拷贝

(1)递归

   obj = { 
            a: {name: 'abc'},
            b: 2 
         }
        function fn(obj) {
            let newObj = {}
            // 如果不是对象直接返回 
            if(typeof obj !=='object'){
                return '不是一个对象'
            }else {
                // 是对象就对每一项进行遍历 
                for (const key in obj) {
                    // 判断内部是否为对象  如果不是对象就进行递归 
                    if(typeof obj[key] ==='object'){
                        newObj[key]=fn(obj[key])
                    }else{
                        newObj[key]=obj[key]
                    }
                }
            }
            return newObj
        }

优化 

  obj = {
            a: { name: 'abc'  },
            b: 2
        }
        function fn(obj) {
            let newObj = {}
            if(typeof obj !=='object') return '不是一个对象'
                for (const key in obj) {
                    typeof obj[key] ==='object'? newObj[key]=fn(obj[key]): newObj[key]=obj[key]
                    }
            return newObj
        }
         let newObj = fn(obj)

 这时候在修改对象里面的引用类型时 就不会影响另一个对象

        newObj.a.name='bcd'
        console.log(obj);
        console.log(newObj);

(2)JSON.parse(JSON.stringify())

     let newObj = JSON.parse(JSON.stringify(obj))

这个深拷贝我们经常会在网上看到,但也不是没有缺陷 

  obj = {
            a:NaN,
            b: undefined,
            c:new Date(),
            d:new RegExp(/\d/),
            d:new Error(),
            e:Infinity
        }
        console.log(obj);
        let newObj = JSON.parse(JSON.stringify(obj))
        console.log(newObj);

对比图

JSON.parse(JSON.stringify())拷贝后的缺陷:

  • NaN          ===》null
  • undefined ===》 空
  • 时间戳      ===》字符串时间
  • 错误信息  ===》 空对象
  • Infinity      ===》null

猜你喜欢

转载自blog.csdn.net/m0_46846526/article/details/119513663
今日推荐