JS的深拷贝和浅拷贝对比
引言: 最近复习一些JS的基础知识点,看到JS深拷贝、浅拷贝发现只有一些模糊的印象。所以决定认真的探讨一下其中的道理。
先说堆和栈
- 有人就会纳闷,为啥扯到了堆和栈?
- 其实深拷贝和浅拷贝最主要的区别就是其在内存中的存储类型。
- 堆和栈都是内存中划分出来用于存储的区域。
栈(stack)为自动分配的内存空间,它由系统自动释放;
堆(heap)则是动态分配的内存,大小不定也不会自动释放。
复制代码
JS基本数据类型
五个基本数据类型:String、Number、Boolean、Undefined、Null
复制代码
基础的数据类型是存放在栈里的
存放在栈中的简单数据类型,数据大小确定,是直接按值存放的,所以可以直接访问。
JS基本数据类型的值不可改变
对这个JS概念不太了解的同学可能会感到诧异。稍安勿躁,我慢慢道来。
基础数据类型和对象、数组、函数有着根本上的区别。
任何函数都无法更改一个原始值。
我们平时认为的修改一个参数的值,其实是定义并返回了一个新的值。
Talk is cheap show me the code.
var str = 'abc'
str[1] = 'd'
str = 'abc'
相信了吧,
所以,记住这一点:基本数据类型值不可变!
复制代码
引用类型(Object)是存放于堆中的
变量其实是存放在栈中的一个指针,指向堆中存放的数据。
和基本数据类型不同,引用类型是可变的。
Talk is cheap show me the code.
var arr = ['a', 'b', 'c']
arr[0] = 'c'
arr = ['c', 'b', 'c']
复制代码
赋值的对比
var a = 1
var b = a
a = a + 1
b = b + 2
a = 2
b = 3
由此可知基础数据类型的赋值是传递值。b开辟了新的内存来存放自己的值。a、b此时是独立的。
var a = {}
var b = a
a.date = '2018-12-12'
a = {name: '2018-12-12'}
b = {name: '2018-12-12'}
由此我们可知,引用类型的赋值其实是传递了指针。a、b都指向了同一个地址。所以改变其中一个,另一个也会随之变化。
复制代码
浅拷贝对比赋值
我还是采用代码来说话吧。(从此代码不是劝退,而是爱!)
var chef1 = {
name: '王刚',
age: 28,
skills: ['炒竹鼠', '麻辣龙虾', '炒鸡蛋']
}
var chef2 = chef1
var chef3 = shallowCopy(chef1)
function shallowCopy(obj) {
var temp = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
temp[prop] = obj[prop]
}
}
return temp
}
chef2.age = 100
chef3.name = '王婆'
chef2.skills[0] = '打篮球'
chef3.skills[1] = '踢足球'
chef1 = {
name: '王刚',
age: 100,
skills: ['打篮球', '踢足球', '炒鸡蛋']
}
chef2 = {
name: '王刚',
age: 100,
skills: ['打篮球', '踢足球', '炒鸡蛋']
}
chef3 = {
name: '王婆',
age: 28,
skills: ['打篮球', '踢足球', '炒鸡蛋']
}
复制代码
如果大家一路看下来,相比看出一些端倪了。
让我们理一下思路:chef1(原数据), chef2(赋值数据), chef3(浅拷贝数据)
- 赋值数据(chef2)和原数据(chef1)因为指向同意地址,所以数据始终保持一致。
- 浅拷贝得到的数据(chef3),name和age是受自己控制,但是层级更深的skills却和原数据保持一致。
- 结论:浅拷贝之所以叫浅拷贝是因为只能拷贝浅层的数据,更深层的数据依然指向原数据。
深拷贝
有了前面的铺垫,我相信大家应该清楚深浅拷贝了。不知道的我就嘤嘤嘤~给你看
顾名思义,深拷贝会将原数据完全拷贝,脱离原数组的控制。
没错,我们将用递归。
你真是太聪明了。
复制代码
双手奉上代码
//深拷贝函数
function deepCopy(p) {
var obj
var str = getType(p)
if(str == 'array') {
obj = []
for (var i = 0; i < p.length; i++) {
obj.push(arguments.callee(p[i])) //回调自己
}
}else if(str == 'object') {
obj = {}
for(var i in p) {
obj[i] = arguments.callee(p[i])
}
}else {
return p
}
return obj
}
// 判断变量的类型
function getType(obj) {
var str = Object.prototype.toString.call(obj)
var map = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Undefined]': 'undefined',
'[object Null]' : 'null',
'[object Object]' : 'object'
}
if(obj instanceof Element) { //判断是否是dom元素,如div等
return "element"
}
return map[str]
}
复制代码
我们下期再见~~~