关于深浅拷贝的原理及实现(超级萌新版)

写一下这几天在看面试题时又回顾到的一个知识点,深浅拷贝。

这个应该是在ES5面向对象之前写过,现在是忘的差不多了,在梳理了一下逻辑以后,决定重新开篇文章写一下。

一、堆与栈

如果是明白堆与栈的,或者是不屑于理解基础知识,只求深浅拷贝怎么写的,可以跳过这部分直接看后面。但我认为理解一个东西,就要贯彻到底,一知半解多半会出问题。

那我们先说一下堆与栈,什么是堆什么是栈呢。在我们创建变量之初,内存会开辟两类空间,就是栈和堆。当然我们知道,变量有两种类型:基本数据类型(包括ES6的symbol数据)和引用类型数据,所以这两种对象分别储存于这两个空间之中。

这样应该很好理解,那么堆与栈的关系呢?这里就要提到一个问题了,在ECMAscript语法中,js是不允许直接访问堆内存的,那么我们要如何取出引用类数据呢,这就要提到地址了。每个引用类数据,都有对应的地址(地址不唯一),每个地址都能指向对应的堆数据。举个例子:

let demo = {id:"xiaoming"}

那么这行代码中,{id:"xiaoming"}就会储存于内存开辟的堆空间中,而demo就会作为地址的变量名存放于栈内存中。如果还没看懂,我画个图,作为对比我再新建几个变量:

let demo = {id:"xiaoming"};

let a = 'hello';

let b = 123

凑合着看吧。。

通过上述图示,应该就能很清楚的展示基本数据类型和引用数据类型的存储区别了,这里再附上一段比较代码,大家参考一下,应该会更加清楚:

var str1 = new String('abc');

var str2='abc';

alert(str1==str2);// true

alert(str1===str2);// false

最后总结一下:
1.在代码运行的时候,每个线程会分配一个堆和一个栈。堆的大小是不固定的,可以随时增加;而栈创建的时候就确定大小,所以可能溢出。

2.堆的空间大,但是运行效率相对较低;而栈相反。

3.栈存放基本类型数据,函数,对象指针等;堆存放对象;

二、递归

同上,只想看深浅拷贝代码的可以跳过了。但这里我还是建议看一下的。

简单来说,递归是一种函数编码思想。用法就是在函数内调用自身,同时为其中增加一个条件来终止代码(不然会无限循环)。举个例子:

var num = 0;

        function foo2(){

            num ++

            console.log("当前的num值:"+num);

            if(num > 5){

                // 终止代码的执行

                return ;

            }

            foo2();

        }

        foo2();

递归是一种理解起来并不难,但实际操作时却需要想半天的东西,只能多写来提高熟练度了。

三、浅拷贝

所谓拷贝,说穿了就是数据的复制,但在js中因为我前面提到的堆和栈的问题,我们如果只是简单的循环,仅仅只能对浅层的数据进行复制,这就是浅拷贝,那么在不涉及嵌套对象的前提下,就对仅仅是基本数据类型的对象进行拷贝。

懒人版浅拷贝方法一

此方法是ES6中提供的,但注意,该方法不会拷贝不可枚举的属性和继承属性。

object.assign

 let demo = {};

        let test = {

            name:'aaa',

            age:123

        }

        Object.assign(demo,test);

        console.log(demo);

这里多一嘴:这种方式不止ES6,在jq中也有提供$.extend()方法,效果是一样的。

懒人版浅拷贝方法二

同样是在ES6中,由语法糖提供的扩展运算符,这个就更简单了。。

demo = {...test}

console.log(demo);

懒人版浅拷贝方法三

看了上面的,觉得浅拷贝真的很简单吧,这里再说一下,同样可以用slice,concat等对数组进行操作(注意splice方法,该方法会对原数组进行切剪)

js原生代码的浅拷贝

function simpleCopy(data) {

            // 新地址, 判断data是数组,还是对象

            var newData = Array.isArray(data)? [] : {}

            // 循环data ,然后复制数据

            for(var key in data){

                // 复制

                newData[key] = data[key]

            }

            // 返回复制完成的数据集合

            return newData;

        }


四、深拷贝

终于到深拷贝了,那么先说一下,深拷贝就不止是对浅层数据的拷贝了,我们要结合上面的堆栈和递归思想,实现原有对象的完全复制。二者实现真正的分离。

懒人版深拷贝方法一

ok,那么按照顺序先放一个懒人版的,我们可以通过JSON来实现深浅拷贝(应该是目前最简单的深浅拷贝了)。

又不能粘贴了。。

递归深拷贝

简单版

这里提出三个问题,有待解决:

1.关于不可枚举的属性以及symbol类型数据的复制

2.Date,RegExp类型

3.关于数据循环引用的问题?

我写了一段,但是没有完全解决,带我搞定了上传代码

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容