前言:在商城项目中,通过vuex实现商品详情页和购物车页面间的状态管理时,发现在添加商品(对象)到vuex后,在购物车页面点击勾选和去勾选时无法实现响应式管理,最终通过添加商品时采用深拷贝JSON.parse(JSON.stringify(goods))的方式解决。
本文部分内容从JS深拷贝和浅拷贝的实现转载
深拷贝与浅拷贝
浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象(fresh copy),新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝
// 只复制第一层的浅拷贝
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
深拷贝的实现方式
- 递归
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子属性为引用数据类型,递归复制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj2[i]); // 递归调用
} else {
// 如果是基本数据类型,只是简单的复制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
- Object.create方式
// Object.create实现深拷贝1,但也只能拷贝一层
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子属性为引用数据类型,递归复制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
// 如果是基本数据类型,只是简单的复制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
- JSON.stringify和JSON.parse结合实现
JSON.stringify把对象转换为字符串,再用JSON.parse把字符串转换成新的对象
function deepCopy(obj1){
let _obj = JSON.stringify(obj1);
let obj2 = JSON.parse(_obj);
return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
//var b= JSON.parse(JSON.stringify(a))
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 2,2,2,3,4
缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function等没办法转成JSON,如果直接序列化会存在以下问题:
1)时间对象 => 字符串的形式
2)RegExp、Error对象 => {}空对象
3)function、undefined会被丢失
4)NaN、Infinity ⇒ null
- 函数库lodash库中的.cloneDeep方法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
- 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