我们在对数据进行备份的时候,如果这个数据是基本的数据类型,那么很好办,通过赋值实现复制即可。如果在使用JavaScript对数组或对象进行操作的时候,我们经常需要将数组或对象进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。这个问题就是深拷贝和浅拷贝的问题。
1.数组的浅拷贝
1.我们可以利用数组一些常用的方法:slice , conact返回一个新的数组进行拷贝
比如:
var arr = ['old', 1, true, null, undefined];
var new_arr = arr.concat();
new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]
var arr = ['old', 1, true, null, undefined];
var new_arr = arr.slice(2);
new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", null, undefined]
2.但是如果数组嵌套了对象或者数组的话,比如:
let obj1 = [{a: '1',b: '2'}]
var arr = [];
arr = arr.concat(obj1);
arr[0].a = "test";
console.log(arr); //[{a: "test", b: "2"}]
console.log(obj1);//[{a: "test", b: "2"}]
我们会发现,无论是新数组还是旧数组都发生了变化,也就是说使用 concat 方法,克隆的并不彻底。
如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。
我们把这种复制引用的拷贝方法称之为浅拷贝,与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。
2.数组深拷贝
我在想如何让obj2复制obj1的对象内容,在我对obj2进行修改时,不影响obj1,下面总结三个个方法,可以在不同情况下使用。
1.JSON.parse(JSON.stringify(obj))
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]
var new_arr = JSON.parse(JSON.stringify(arr));
new_arr[4].old="test";
console.log(new_arr);// ['old', 1, true, ['old1', 'old2'], {old: "test"}]
console.log(arr);// ['old', 1, true, ['old1', 'old2'], {old: 1}]
image.png
是一个简单粗暴的好方法,就是有一个问题,不能拷贝函数。
2. Object.assign(target, …sources)
Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。我们可以使用它进行对象的复制、合并。
// 复制
let c = Object.assign({}, { a: 'apple' });
console.log(c); // { a: 'apple' }
//合并
let o = {}
let c = Object.assign(o, { a: 'apple' }, { b: 'banana' }, { c: 'cake' } )
console.log(c) // { a: 'apple', b: 'banana', c: 'cake' }
虽然它可以复制对象,但是 Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
3.深浅拷贝实现原理
以上三个方法 concat、slice、JSON.stringify 都算是技巧类,可以根据实际项目情况选择使用,接下来我们思考下如何实现一个对象或者数组的浅拷贝。
想一想,遍历对象,然后把属性和属性值都放在一个新的对象不就好了
浅拷贝原理
var shallowCopy = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是对象
var newObj = obj instanceof Array ? [] : {};
// 遍历obj,并且判断是obj的属性才拷贝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
一段真正深拷贝的代码(解决以上深拷贝遗留问题)
那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
let obj1 = {
a: '1',
b: '2',
c: {
d: '3'
},
d: function aa() {}
}
function deepCopy(obj) {
if(typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
var tes=deepCopy(obj1);
console.log(tes)
obj1.c.d=32;
console.log(obj1)
image.png
网友评论