什么是浅拷贝
如果数据是基本数据类型,在浅拷贝的时候就如同于直接赋值,是拷贝其本身的值;但是如果除了基本数据类型之外还有一层对象,对于浅拷贝而言是拷贝引用,也就是拷贝的是地址,从而会导致改变了一方其他也都被改变的情况。
实现浅拷贝有哪些方法呢?
Object.assign
可以做到,Object.assign是ES6的新函数。Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。Object.assign(target,...sources)
Object.assign
只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址。
let obj1 = {
a: 1,
b: {
a: 1,
b: "hello"}
}
let obj2 = Object.assign({}, a)
obj2.a = 2;
obj2.b.a = 2;
console.log(obj1.a) // 1
console.log(obj1.b.a ) // 2
以上代码中,Object.assign
将obj1的属性值拷贝到obj2中,obj1中的b属性是对象,所以拷贝b属性的时候是拷贝其地址,就出现了一方改变了值其他也都会被改变。而a属性是基本数据类型,相当于拷贝了本身的值,有自己的内存空间的。别人改变对它是没有影响的。总的来说,浅拷贝就是拷贝了一层,除了对象是拷贝的引用类型,其他都是直接将值传递,有自己的内存空间的。
另外我们还可以通过展开运算符...来实现浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
const originArr = [1,2,3,[4,5,6]];
const originObj = {a:1,b:{cc:1}};
const cloneArr= [...originArr];
cloneArr[0] = 0;
cloneArr[3].push(7);
console.log(originArr); // [1,2,3,[4,5,6,7]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.cc = 2;
console.log(originObj); // {a:1,b:{cc:2}}
用展开运算符对数组或者对象进行拷贝时,只能展开和深拷贝第一层的值,对于第二层及其以后的值,展开运算符将不能对其进行展开,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。
什么是深拷贝?
深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。
实现浅拷贝有哪些方法呢?
通过JSON.parse(JSON.stringify(object))
来解决。JSON.stringify()
的作用是将 JavaScript 值转换为 JSON 字符串,而JSON.parse()
可以将JSON字符串转为一个对象。
let a = {
age: 1,
jobs: {
first: 'first'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'changed'
console.log(b.jobs.first) // first
这种方法的原理其实是:不拷贝引用对象,拷贝一个字符串会新辟一个新的存储地址,这样就切断了引用对象的指针联系。总而言之,要实现深拷贝,就是通过实例化一个新的对象,从而在堆中开辟一块新的内存空间,使得栈中的变量名指向堆中的新内容。
但是这种方法也有不少坏处,比如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。这种方法能正确处理的对象只有Number,String,Boolean,Array,扁平对象,即那些能够被 json 直接表示的数据结构。
递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
总结
- 浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;
- JSON.stringify实现的是深拷贝,但是对目标对象有要求(非 undefined,function);
- 若想真正意义上的深拷贝,只能递归拷贝。