zoukankan      html  css  js  c++  java
  • 深拷贝和浅拷贝

    继续研究基础知识,老菜鸟真的玩不起了,加油吧,腊肉!

    初学的时候,没理解深浅拷贝,现在玩代码真的是力不从心,决定重新学习基础知识!

    我们代码中常常这么写:

    var a = '1234'
    var b = a
    b = '5678'
    console.log(a, b) // '1234'   '5678'
    

      结果是: a是a,b是b,两个变量之间没有任何影响,一旦被赋值,就不会发生改变,但是看看数组如果也这么玩,会是什么结果?

    var a = [1,2,3,4]
    var b = a
    b[0] = 5
    console.log(a, b)
    

     我们来看结果:

    a和b两个数组都发生了变化,但是我们没有直接改动a啊,怎么a也发生了变化?对于我这个小学生来说,真是一头雾水。

    今天趁着有时间,查了资料,发现用“堆栈”的概念去理解会更好记忆一些。

    我们知道,js中数据类型,包括基本数据类型: string  number  boolean undefined  null 以及ES6中的symbol还有起草的ES10中BigInt类型;第二类是引用数据类型:object对象  数组以及function等。

    那么基本数据类型和引用数据类型,既然起了两个名字,那实现的方式肯定也不一样,顺着这个思路,我们来看看究竟是怎么回事:

    我们简单的从“堆栈”的角度看看,堆,可以简单理解成是“程序员”给它分配的内存空间,“被动”被占用的,而栈是系统主动分配的空间。
    基本数据类型的操作都是在栈中执行的,而引用数据类型则是在堆中操作的:

    引用数据类型是怎么操作的呢?

     

    我们的引用类型的数据是存放在堆中的,你看到的var a = [1,2,3,4]只是a 去堆中引用过来的,本质上a只具有显示成[1,2,3,4]的样子,并没有实际的操作能力。既然a都没有实际的能力,那b就更不用说了,同样也只是显示成[1,2,3,4]的样子罢了,不管对a操作还是对b操作,操作的都是堆内存中的实际数据。


    来看看a和b数组是不是同一个东西:

    var a = [1,2,3,4]
    var b = a
    console.log(a === b) // true
    

     好吧,这个结果更加验证了,a和b只是显示堆中的数组[1,2,3,4].

    再来看一个例子:

    var a = [1,2,3,4]
    var b = [1,2,3,4]
    console.log(a === b) // false
    

    why?为啥不相等了呢?画个图就明白了:

    是不是清楚了?这种情况下,a和b分别去不同的堆内存中去取数据了,当然是两个不一样的东西了啊。

    但是两个外形一样的字符串,是不是相等的?当然相等!没有涉及到更深层的堆,所以只是字面量上的比较,结果相等。

     

    好,弄清楚了基本数据类型和引用数据类型的表现,现在开始看拷贝的问题:
    再看一个例子:

    var a = {
    	key1: '1-1',
    	key2: '1-2',
    	key3: [1,1,1]
    }
    var b = a // a赋值给b
    function copy(obj) {
    	var result = {}
    	for (var key in obj) {
    		if (key && obj[key]) {
    			result[key] = obj[key]
            }
    	}
    	return result
    }
    var c = copy(a)// a浅拷贝给c
    
    b.key1 = '2-1'
    c.key2 = '3-2'
    
    b.key3[1] = 2
    c.key3[2] = 3
    
    console.log(a, b, c)
    

      

    结果如下:

    可以得到如下结论:
    1、给b.key1重新赋值,会影响a.key1的值,但是给c.key2从新赋值,则不会影响a.key2的值,我们得出结论:对象的直接赋值给新对象,新的对象中某基本数据类型的数据还是指向原来对象的值,是同一个量,而通过浅拷贝的基本数据类型,则是一个新的量,与原对象中的数据无关;

    2、无论是赋值,还是浅拷贝,引用类型的数据,改变一处,所有引用该数据的地方都会发生改变。这是因为浅拷贝只复制基本数据类型(没有下级数据)的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的c中的引用类型时,会使原来对象得到改变。

    既然知道了原因,那我们现在可以考虑如何才能做到把c变成一个彻彻底底的不影响原来对象的独立对象呢?这正是深复制要做的事情,在浅复制的基础上,把下级数据也复制上就行啦:

    function deepCopy(obj,result){
        var result = result || {} // 局部变量result赋初值为接收的参数或者为一个空对象。
        for(var key in obj){            
        	if(typeof obj[key] === 'object'){ // 依次判断obj对象的属性是不是对象
                result[key] = (Array.isArray(obj[key])) ? [] : {} // 判断要复制的项是对象还是数组
                deepCopy(obj[key],result[key]) // 递归实现,重点也在这里
            } else {
                result[key] = obj[key] // 如果不是的可以直接相等
            }
        }
        return result
    }
    var obj = {a: '1-1', b: [1,1]}
    var obj1 = deepCopy(obj, {})
    obj1.a = '2-1'
    obj1.b = [2,2]
    console.log(obj, obj1)
    

     看结果: 

    看来这次改动复制得到的新对象,对原来的对象没有影响了,深复制成功!

    当然,如果数据中不含有以下类型,也可以使用JSON.parse(JSON.stringify())进行深复制。

    a.时间对象: Date() 被处理后会变成字符串
    b.RegExp、Error对象 被处理成空对象
    c.undefined 被处理丢掉
    d.NaN、Infinity、-Infinity 被处理成null
    e.构造函数 丢掉构造函数,以“Object”代替
    f.有循环引用的数据

    最后总结一下,赋值,浅拷贝和深拷贝的区别:

      是否指向原对象 修改基础数据类型是否影响原对象 修改引用类型是否影响原对象
    赋值(=)
    浅拷贝
    深拷贝

    当然看其他博客中也介绍了jquery的$.extend() http://www.runoob.com/jquery/jquery-ref-misc.html方法,有兴趣的同学可以自己研究一下,毕竟封装起来的东西,通用性更广。

  • 相关阅读:
    css 修改input中placeholder提示问题颜色
    js 获取屏幕或元素宽高...
    js时间相关
    golang——gRPC学习
    golang——win10环境protobuf的使用
    golang——net/rpc/jsonrpc包学习
    golang——net/rpc包学习
    golang——log包学习
    golang——database/sql包学习
    mysql——免安装配置
  • 原文地址:https://www.cnblogs.com/whq920729/p/10662489.html
Copyright © 2011-2022 走看看