JS深拷贝与浅拷贝
YinJie… 人气:0一、JS中数据的存储形式-堆栈
我们先简单理解一下堆栈分别是啥:
什么是栈:计算机为原始类型开辟的一块内存空间 string number ...
什么是堆:计算机为引用类型开辟的一块内存空间 object
我们分别分析下面两段代码:
var a = 'jack' var b = a b = 'andy' console.log(a,b);//jack andy
var c = {key : 1} var d = c d.key = 2 console.log(c,d);//{ key: 2 } { key: 2 }
看完之后我们可能有这么一个疑问,第一段代码很好理解,但是第二段代码里改变的明明是d中的key,为啥c里的key也改变了呢?
这里就是因为堆跟栈的原理,我们在定义原始类型数据的时候都会开辟一个栈的空间,当声明一个基本变量时,它就会被存储到栈内存中,而当其发生复制时,会把对应内存中的数据复制一份到新内存中,所以这两个变量之间没有什么联系。如果我们对引用类型进行复制,我们只是将地址复制了一遍,原变量和复制的新变量都是指向同一个地址,这就说明对新变量进行修改时就会影响到原变量的值。
二、深浅拷贝的三种方式
- 遍历赋值
- Object.create()
- JSON.parse()和JSON.stringify()
深拷贝与浅拷贝,简单点来说:
就是假设B赋值了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝。如果B没变,那就是深拷贝。
遍历赋值
下面我们看一段代码:
var obj = { a:'hello', b:{ a:'world', b:111 }, c:[11,'jack','abdy'] } function clone(obj) { var objnew = {} for(var i in obj) { objnew[i] = obj[i] } return objnew } var objcopy = clone(obj) console.log(obj); console.log(objcopy);
在这里我们定义了一个对象 obj,为了实现能够复制出另一个对象,我们再定义一个clone方法。然后调用这个方法,输出原来的对象和复制后的对象,看看是否相同:
在控制台下输出的两个结果没有任何差异,那他们两个是否真的完全一样呢?
他们两个是有不同的,因为这里的拷贝属于浅拷贝,我们根据浅拷贝的定义,如果在上例中,我们改变了objcopy,那么obj也发生了变化的话那他就是一个浅拷贝。
我们下面来验证一下,改变一下objcopy.b.a的值,看看 obj 里会不会发生相应的变化:
将objcopy.b.a赋值为字符串hhhh后,obj.b.a也变为了hhhh,这就说明我们最开始的拷贝属于浅拷贝。因为obj.b他是一个引用类型,所以objcopy.b和obj.b指向的都是同一个地址,这样不管objcopy.b.a改成什么,obj.b.a都会变成什么。
Object.create()
这种方法只需要行代码:
var objcopy = Object.create(obj)
obj在复制的时候,它会被当前对象复制到原型__proto__上,并不是复制到当前的对象上。我们再次改变一下objcopy.b.a的值,看看 obj 里会不会发生相应的变化:
所以他也是浅拷贝
遍历赋值实现深拷贝
这是深拷贝的克隆函数:
function deepclone(startobj,endobj) { var obj = endobj || {} for(var i in startobj) { if(typeof startobj[i] === 'object') { obj[i] = startobj[i].constructor === Array ? [] : {} deepclone(startobj[i],obj[i]) }else { obj[i] = startobj[i] } } return obj }
值得注意的一点是,在递归调用的时候,需要把当前处理的 obj[i] 给传回去,否则的话 每次递归obj都会被赋值为空对象,就会对已经克隆好的数据产生影响。
我们来验证一下是否是深拷贝:
var objcopy = deepclone(obj) objcopy.b.a = 'hhhh' console.log(obj); console.log(objcopy);
运行结果:
这就说明我们的深拷贝已经实现了。
通过JSON.parse()和JSON.stringify()实现深拷贝
这是我们在工作中最常见的一种方式
var objcopy = JSON.parse(JSON.stringify(obj))
我们对初始化的对象先通过JSON.stringify转换为字符串,再通过JSON.parse转回对象类型。
这样为什么能实现深拷贝的效果呢?因为我们通过 JSON.stringify 转成 string 类型后,他就存储在栈里,这样就不会出现地址值上的误会了,再通过JSON.parse就可以再转换为object类型。
加载全部内容