前言
在使用JavaScript中有很多方法可以进行复制对象,但是在使用过程中要千万小心。你可能一不小心就掉入设计好的陷阱之中。
阅读完本文,你将学会如何正确的复制对象
首先我们要了解深/浅拷贝,它们与赋值之间存在着那些区别
浅拷贝
浅拷贝顾名思义就是创建一个新的对象,然后将别人的对象属性值进行一次精准的拷贝,如果属性是基本类型,所拷贝的内容就是基本类型的值。如果拷贝的是引用类型,拷贝的只是内存地址,它与原对象指向同一个内存空间。
深拷贝
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新的对象不会影响到原对象。
浅拷贝就是和别人共同租房,共用同一片天地,你在客厅吃了一份臭臭的臭豆腐,你的同居室友也会受到影响。
深拷贝就是你看到好友要买房子,房子非常的漂亮,也符合你的要求,而且你还想和他做个邻居,然后你就在他家附近买了一套一模一样户型的房子。
这样大家是不是就很容易理解了。
接下来直接上代码展示
浅拷贝
let a = {
name: '漩涡鸣人',
age: 18
}
let b = a;
console.log('a的结果是', a);
console.log('b的结果是', b);
b.name = '宇智波佐助'
console.log('修改新对象内容之后a的结果是', a);
console.log('修改新对象内容之后b的结果是', b);
复制代码
咦?明明是复制了一份,为什么修改b的属性值,a也跟着变了。
原来我们拷贝的是一个对象,它们共同使用同一个存储空间。
let a = '漩涡鸣人'
let b = a;
console.log('a的结果是', a);
console.log('b的结果是', b);
b = '宇智波佐助'
console.log('修改新对象内容之后a的结果是', a);
console.log('修改新对象内容之后b的结果是', b);
复制代码
看,如果我们拷贝的只是一个基本属性,我们对b进行修改将不会影响到a。
①基本类型:直接存储在栈中的数据。(字符串、布尔值、未定义、数字、null)
②引用类型:将该对象引用地址存储在栈中,然后对象里面的数据存放在堆中。(数组、对象、函数)
如果我们拷贝的是引用类型就要进行注意,只是单纯的复制那可不行,它们使用的还是同一个内存空间,这时我们就需要进行深拷贝,让他们互不干扰,拥有一片属于自己的小天地。
深拷贝
let a = {
name: '漩涡鸣人',
age: 18
}
let b = JSON.parse(JSON.stringify(a)); //系统自带的JSON方法
console.log(a);
console.log(b);
b.name = '宇智波佐助'
console.log('修改新对象内容之后a的结果是', a);
console.log('修改新对象内容之后b的结果是', b);
复制代码
系统自带的JSON方法可以进行深拷贝,但是有一个缺点 函数无法使用此方法进行深拷贝。
let a = {
name: '漩涡鸣人',
age: 18,
jineng: function () {
console.log('豪火球之术');
}
}
let b = Object.assign({}, a)
console.log(a.jineng);
console.log(b.jineng);
b.jineng = function () {
console.log('写轮眼');
}
console.log('修改新对象内容之后a的结果是', a.jineng);
console.log('修改新对象内容之后b的结果是', b.jineng);
复制代码
Obje.assign当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
var obj = {
a: 1,
b: {
c: 2,
d: 3
}
}
function deepCopy(obj) {
// 递归 -- 自己调用自己
var cloneObject = {}
for (let key in obj) {
if (obj[key] instanceof Object) {
// cloneObject['b'] = {c:2,d:3}
cloneObject[key] = deepCopy(obj[key])
} else {
cloneObject[key] = obj[key]
}
}
return cloneObject;
}
var a = deepCopy(obj)
//修改新对象中的数值
a.b.c = 5
console.log(obj)
console.log(a);
复制代码
深拷贝需要使用递归方式才可以就行拷贝
深度优先遍历和广度优先遍历
其实简单来说 深度优先就是自上而下的遍历搜索 广度优先则是逐层遍历。
两者的区别
对于算法来说 无非就是时间换空间 空间换时间
- 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
- 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点
深度优先采用的是堆栈的形式, 即先进后出
广度优先则采用的是队列的形式, 即先进先出
const data = [{
name: '小明',
parents: [{
name: '明爸爸',
parents: [{
name: '明爷爷'
}]
},
{
name: '明妈妈',
parents: [{
name: '明姥姥'
}]
},
],
},
{
name: '小红',
parents: [{
name: '红爸爸',
parents: [{
name: '红爷爷'
}]
},
{
name: '红妈妈',
parents: [{
name: '红姥姥'
}]
},
],
},
]
function getName(data) {
const result = [];
data.forEach(item => {
const map = data => {
result.push(data.name);
data.parents && data.parents.forEach(parent => map(parent));
}
map(item);
})
return result.join(',');
}
// 广度遍历, 创建一个执行队列, 当队列为空的时候则结束
function getName2(data) {
let result = [];
let queue = data;
while (queue.length > 0) {
[...queue].forEach(parent => {
queue.shift();
result.push(parent.name);
parent.parents && (queue.push(...parent.parents));
});
}
return result.join(',');
}
console.log('深度---->' + getName(data))
console.log('广度---->' + getName2(data))
复制代码