js 在原生中并不具备深拷贝的功能,甚至不具备拷贝功能,大多是使用其他函数模拟效果,所以这里进行深入探讨。
现状
js 中有
简单类型:1. 字符串 2. 数值 3. 布尔值 4. null 5. undefined
引用类型:对象(object)
同时,引用类型又分为几个特殊型:1. Object 2. Array 3. RegExp 4. Date 5. Function
以及单体内置对象:1. Global 2. Math
简单类型的拷贝很简单,因为数据储存在栈内存,拷贝实际就是内存的复制,并用新变量承载;
1 | var s1 = 'string'; |
引用类型则要复杂一些,因为 js 的内存分为 栈内存
和 堆内存
,引用类型实际内容是储存在堆内存中,声明一个新变量使用等号仅仅是指向了同一片堆内存,所以会发生:
1 | var o1 = {a: 1} |
看上去,改变 o1 的值,o2 的值也跟着变,但实际上,o1 和 o2 都指向同一片堆内存,就和我叫我的弟弟 傻康,但我妈叫他 亲宝贝,实际两个名字代表的是同一个人。
那要如何按预想中复制变量呢?
深拷贝
事实上引用类型的拷贝有深浅两种,浅拷贝指的是只拷贝引用类型的第一层,也就是,如果引用类型的内部变量指向了另一个引用类型,那内部变量指向的引用没有被拷贝,就称为浅拷贝。
1 | // 数组 |
使用 slice 方法切片切整个达到复制目的,并原数组不变;
数组同样可以使用 concat 方法,concat 方法连接接数组,不传参数时,就只返回自己,原数组也不变,达到复制目的
1 | // 对象 |
显然对象使用 Object.assign 方法不能开辟新内存;
想要无脑浅拷贝,可以使用 Json 对象的转换方法
1 | var obj1 = {a: 1, b: 2} |
引用类型的深拷贝
那想要内部的引用类型也拷贝怎么办,递归遍历他
1 | var a = [1,2,{c: 1}] |
显然就可以了。
另外在 jquery 中,框架已经帮我们实现了相关函数:extend
1 | var a = [1,2,{c: 1}] |
dom 元素的拷贝
dom 元素的复制比想象中还要简单,早在 DOM 2.0 就加入了 cloneNode 方法,第一个参数设置为 true,就可以拷贝元素的子元素。
1 | var _body = document.body.cloneNode(); |
但并没有复制元素“已经绑定的事件”,万能的 jq 同样封装了 clone 方法,第一个参数传入 布尔值,就可以复制事件了。
1 | var _body = $(body).clone(); |