js之深拷贝与浅拷贝

一、深拷贝与浅拷贝

  • 什么是深拷贝 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象(新旧对象不共享同一块内存),且修改新对象不会影响原对象(深拷贝采用了在堆内存中申请新的空间来存储数据,这样每个可以避免指针悬挂

  • 什么是浅拷贝 如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,那么拷贝的就是内存地址(新旧对象共享同一块内存),所以如果其中一个对象改变了这个地址,就会影响到另一个对象(只是拷贝了指针,使得两个指针指向同一个地址

二、赋值与浅拷贝的区别

  • 把一个对象赋值给一个新的变量的时候,赋的其实是该对象在栈中的地址,而不是栈中的数据。也就是这两个对象指的是同一个储存空间,不论哪个对象发生改变,其实都是改变储存空间里的内容。因此,两个对象是联动的。
  • 浅拷贝是按位拷贝对象的,他会创建一个新的对象,这个对象有着原始对象属性值的一份精确拷贝。

如果属性是基本类型,拷贝的就是基本类型的值。
如果属性是内存地址(引用类型),拷贝的就是内存地址,因此如果一个对象改变了这个地址,就会影响到另一个对象。
即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

  • 赋值与浅拷贝举例

      对象赋值:
    
      let obj1 = {
    	name:'chen',
    	age: 26,
    	hobby:['baskitball','swim','run','eat']
      }
      let obj2 = obj1
      obj2.name = 'zi'
      obj2.age = 100
      obj2.hobby[0] = 'play games'
      console.log(obj1) // {"name":"zi","age":100,"hobby":["play games","swim","run","eat"]}
      console.log(obj2) // {"name":"zi","age":100,"hobby":["play games","swim","run","eat"]}
    

      浅拷贝:
    
      obj1 = {
      	name:'chen',
      	age: 26,
      	hobby:['baskitball','swim','run','eat']
      }
      obj3 = {...obj1}
      obj3.name = 'zi'
      obj3.age = 100
      obj3.hobby[0] = 'play games'
      console.log(obj1)  //{"name":"chen","age":26,"hobby":["play games","swim","run","eat"]}
      console.log(obj3)  //{"name":"zi","age":100,"hobby":["play games","swim","run","eat"]}
    

    obj1是原数据,obj2是直接赋值得到的数据,obj3是通过浅拷贝得到的。

    -- 和原数据是否指向同一对象 第一层数据未基本数据类型 原数据包含子对象(引用数据类型)
    赋值 赋值后的数据改变,会使原数据一同改变 赋值后的数据改变,会使原数据一同改变
    浅拷贝 浅拷贝后的数据改变,不会使原数据一同改变 赋值后的数据改变,会使原数据一同改变

三、浅拷贝的实现【当拷贝对象只有一层的时候,是深拷贝

  • 展开运算符...

      obj1 = {
      	name:'chen',
      	hobby:['baskitball','swim','run','eat']
      }
      obj2 = {...obj1}
      obj2.name = 'zi'
      obj2.hobby[0] = 'play games'
      console.log(JSON.stringify(obj1))  //{"name":"chen","age":26,"hobby":["play games","swim","run","eat"]}
      console.log(JSON.stringify(obj2))  //{"name":"zi","age":100,"hobby":["play games","swim","run","eat"]}
    
  • Object.assign()

      obj1 = {
      	name:'chen',
      	hobby:['baskitball','swim','run','eat']
      }
      obj2 = Object.assign({},obj1)
      obj2.name = 'zi'
      obj2.hobby[0] = 'play games'
      console.log(JSON.stringify(obj1))  //{"name":"chen","age":26,"hobby":["play games","swim","run","eat"]}
      console.log(JSON.stringify(obj2))  //{"name":"zi","age":100,"hobby":["play games","swim","run","eat"]}
    

> 当object只有一层的时候,是深拷贝;所以当原数据进行浅拷贝,改变obj2的name 原数据obj1中的name不会改变;

  • Array.prototype.concat()

       arr1 = [
       	{
               name:'chen'
       	},
       	'baskitball',
       	'swim',
       	'run',
       	'eat'
       ]
       arr2 = arr1.concat([]);
       arr2[0].name = 'zi'
       arr2[1]= 'play games'
    
       console.log(JSON.stringify(arr1))  //[{"name":"zi"},"baskitball","swim","run","eat"]
       console.log(JSON.stringify(arr2))  //[{"name":"zi"},"play games","swim","run","eat"]
    
  • Array.prototype.slice()

      arr1 = [
          {
              name:'chen'
          },
          'baskitball',
          'swim',
          'run',
          'eat'
      ]
      arr2 = arr1.slice();
      arr2[0].name = 'zi'
      arr2[1]= 'play games'
    
      console.log(JSON.stringify(arr1))  //[{"name":"zi"},"baskitball","swim","run","eat"]
      console.log(JSON.stringify(arr2))  //[{"name":"zi"},"play games","swim","run","eat"]
    

> 当Array只有一层的时候,是深拷贝;所以当原数据进行浅拷贝,改变arr2的arr[1],而原数据arr1中的arr1[1]没有改变;

三、深拷贝的实现

  • JSON.parse(JSON.stringify())

      obj1 = {
          name:'chen',
          hobby:['baskitball','swim','run','eat']
      }
      obj2 = JSON.parse(JSON.stringify(obj1))
      console.log(obj1 === obj2)  //false
      obj2.hobby[0] = 'play games'
      obj2.name = 'zi'
      console.log(JSON.stringify(obj1))  //{"name":"chen","hobby":["baskitball","swim","run","eat"]}
      console.log(JSON.stringify(obj2))  //{"name":"zi","hobby":["play games","swim","run","eat"]}
    

> 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

  • 手写递归方法:(递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝)

      function cloneDeep(obj) {
      	//数据类型为引用数据类型
      	if (typeof obj == 'object') {
      		//初始化返回结果
    		let result = Array.isArray(obj) ? [] : {};
    		fot(let key in obj) {
    			//避免相互引用出现死循环导致爆栈
    			if (obj === obj[key]) {
    				continue
    			}
    			if (obj.hasOwnProperty(key)) {
    				//递归调用
    				result[key] = deepClone(obj[key])
    			}
    		}
    		return result
      	} else {
      		//基本数据类型,直接返回
      		return obj
      	}
      }
    

> 这个方法其实也是有缺陷的,没法拷贝一些特殊对象(如 new Map() )

  • jQuery的extend方法实现深拷贝

      var array = [1,2,3,4];
      var newArray = $.extend(true,[],array); // true为深拷贝,false为浅拷贝
    
  • 函数库lodash的_.cloneDeep方法

      var _ = require('lodash')
      var obj = {
        a: {
          c: 2,
          d: [9, 8, 7]
        },
        b: 4
      }
      var obj1 = _.cloneDeep(obj)
      console.log(obj === obj1);//false
    ```</em></p>
posted @ 2022-08-30 11:43  小阿紫  阅读(1349)  评论(0)    收藏  举报