项目上遇到这么一个问题:操作页面上列表中的数据源,当时为了不影响原数据源,就通过解构的方式对数据源进行拷贝,然后操作新的数据源,但是发现最终还是会影响到原来的数据源。于是,就想到可能是对对象进行浅拷贝导致的,所以,就想深入了解下js中关于对象的浅拷贝和深拷贝的原理。
- js的基本数据类型
- 基本数据类型:String,Boolean,Number,Undefined,Null;
- 引用数据类型:Object(Array,Date,RegExp,Function)
- 基本数据类型和引用数据类型的区别:
保存位置不同:基本数据类型保存在栈内存中,引用数据类型保存在堆内存中,然后在栈内存中保存了一个对堆内存中实际对象的引用,即数据在堆内存中的地址,JS对引用数据类型的操作都是操作对象的引用而不是实际的对象,如果obj1拷贝了obj2,那么这两个引用数据类型就指向了同一个堆内存对象,具体操作是obj1将栈内存的引用地址复制了一份给obj2,因而它们共同指向了一个堆内存对象。
- 浅拷贝
- 深拷贝和浅拷贝简单解释
浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制。
// 只复制第一层的浅拷贝
function simpleCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
for (let i in obj1) {
obj2[i] = obj1[i];
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
- Object.assign()实现浅拷贝及一层的深拷贝
let obj1 = {
a: {
b: 1
},
c: 2
}
let obj2 = Object.assign({},obj1)
obj2.a.b = 3;
obj2.c = 3
console.log(obj1.a.b); // 3
console.log(obj2.a.b); // 3
console.log(obj1.c); // 2
console.log(obj2.c); // 3
- 深拷贝
对对象的属性进行递归复制, js中实现深拷贝的几种方法
- 使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象
缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON
let obj1 = {
fun:function(){
alert(123);
}
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined
- jquery实现深拷贝
jquery 提供一个$.extend可以用来做深拷贝
var $ = require('jquery');
var obj1 = {
a: 1,
b: {
f: {
g: 1
}
},
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
- slice是否为深拷贝
// 对只有一级属性值的数组对象使用slice
var a = [1,2,3,4];
var b = a.slice();
b[0] = 2;
alert(a); // 1,2,3,4
alert(b); // 2,2,3,4
// 对有多层属性的数组对象使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4
结论:slice()并非深拷贝,concat()亦是如此。
参考文章: JS深拷贝和浅拷贝的实现