为什么不问问神奇的海螺呢?|

小阿紫

园龄:3年5个月粉丝:8关注:1

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>

本文作者:小阿紫

本文链接:https://www.cnblogs.com/chenzilong/p/16638722.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   小阿紫  阅读(1341)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 人世间 Audio artist
  2. 2 风吹过八千里 Audio artist
风吹过八千里 - Audio artist
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

风吹过八千里 - 藤柒吖

词:巴欧特

曲:Moni鱼子酱

原唱:苏星婕

编曲:东阳

(未经许可,不得翻唱或使用)

有些话何必说清没有意义

请你当做我自作多情

请你当做我自作多情

一人完成两个人的不通关游戏

一人完成两个人的不通关游戏

为何你还不满意

为何你还不满意

冰箱里的东西早就过期

冰箱里的东西早就过期

留言还是周一

到夜深人静

只剩我和空气 没道理

你说爱情仅此而已

谁还没有一丝委屈

不再关心我的世界下了雨

像风吹过八千里

像风吹过八千里

流云和月都曾爱过你

流云和月都曾爱过你

可是潮汐干涸在有情人的海底

那最潮湿的爱意

那最潮湿的爱意

携裹着最伤人语句

携裹着最伤人语句

或许遥不可及 才得人心

或许遥不可及 才得人心

冰箱里的东西早就过期

冰箱里的东西早就过期

留言还是周一

到夜深人静

只剩我和空气 没道理

你说爱情仅此而已

谁还没有一丝委屈

不再关心我的世界下了雨

像风吹过八千里

像风吹过八千里

流云和月都曾爱过你

流云和月都曾爱过你

可是潮汐干涸在有情人的海底

那最潮湿的爱意

那最潮湿的爱意

携裹着最伤人语句

携裹着最伤人语句

或许遥不可及 才得人心

像风吹过八千里

像风吹过八千里

流云和月都曾爱过你

流云和月都曾爱过你

可是潮汐干涸在有情人的海底

那最潮湿的爱意

那最潮湿的爱意

携裹着最伤人语句

携裹着最伤人语句

或许遥不可及 才得人心

或许遥不可及 才得人心