JavaScript的深拷贝和浅拷贝
原文
大纲
前言
1、对深拷贝和浅拷贝的初步认识
2、深拷贝和浅拷贝的区别
3、浅拷贝存在的缺陷
4、深拷贝的实现方式
前言
对于很多初次接触JavaScript的读者来说,想要实现对象的拷贝/复制,就是单纯的使用赋值语句(=)来实现需求。但是,这样的方式是不全面的,这样的方法对于简单类型的数据来说是没有问题的,但是如果是对于像对象这样的复杂的数据类型来说,这样的实现方式有就会给你带来意想不到的问题。那么,如果不能简单的用赋值语句实现对象的拷贝(复制),我们应该用什么样的方式呢?这个,就是深拷贝和浅拷贝的意义所在了。
1、对深拷贝和浅拷贝的初步认识
深拷贝和浅拷贝只针对像 Object, Array 这样的复杂对象的。简单来说,浅拷贝只拷贝一层对象的属性,而深拷贝则递归拷贝了所有层级。
对于字符串类型,浅拷贝是对值的复制,对于对象来说,浅拷贝是对对象地址的复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变,而深拷贝则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
2、深拷贝和浅拷贝的区别
虽然两者都是对内容的拷贝,但是拷贝的内容的不同是区分两者的关键,而这个不同主要体现在对Object和Array这样的复杂对象。最直接的原因是诸如字符串这样简单的类型的拷贝就是内容的复制,并不会有别的什么深层次的东西,但是对于Objcet和Array这样复杂的类型,主要是由于对象的复制是基于对对象的地址的复制而不是整体内容的复制(对象包含了key ,value)。
简而言之:浅拷贝之后的对象实际上都是同一个对象,而深拷贝之后的对象都是除了内容相同以外完全无关的对象。
3、浅拷贝存在的缺陷
通过一个实例来说明使用浅拷贝对对象进行复制会导致哪些问题,从而正确认识使用深拷贝和浅拷贝的重要意义。
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
/*
导致的结果就是:
对shallowObj对象的更改也会影响到原对象obj,即所谓的牵一发而动全身。
*/
shallowObj.arr[1] = 5;
obj.arr[1] // = 5
4、深拷贝的实现方式
以下介绍两种我平时收集到并且常用的深拷贝的实现方式,希望能对读者有所帮助。
4.1、通过JSON解析解决
针对纯 JSON 数据对象的深复制,使用 JSON 全局对象的 parse 和 stringify 方法来实现深复制也算是一个简单讨巧的方法。然而使用这种方法会有一些隐藏的坑,它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。
var test = {
person: {
name: {
first: 'chen',
last: 'kk'
},
age: 12
},
interest: ['book', 'run', 'jump']
}
var result = JSON.parse(JSON.stringify(test))
result.interest.push('sing')
console.dir(test)//['book', 'run', 'jump']
console.dir(result)//friend: ['book', 'run', 'jump','sing']
4.2、通过递归解析解决
想要获得多层的数据结构的数据的深拷贝,不得不考虑使用迭代的方式。下列的方法将需要考虑深拷贝的对象、Array数据类型,并不断的通过迭代判断来对这两种类型的数据进行深拷贝。
var china = {
nation: '中国',
birthplaces: ['北京', '上海', '广州'],
skincolr: 'yellow',
friends: ['sk', 'ls']
}
//深复制,要想达到深复制就需要用递归
function deepCopy(o, c) {
var c = c || {}
for (var i in o) {
if (typeof o[i] === 'object') {
//要考虑深复制问题了
if (o[i].constructor === Array) {
//这是数组
c[i] = []
} else {
//这是对象
c[i] = {}
}
deepCopy(o[i], c[i])
} else {
c[i] = o[i]
}
}
return c
}
var result = {
name: 'result'
}
result = deepCopy(china, result)
console.dir(result)