一.深拷贝与浅拷贝
1.在JS中数据类型有两种:基本数据类型和引用数据类型。基本数据类型包括:Number、String、Boolean、null、undefined、Symbol。引用数据类型包括:Object、Function、Array等。
基本数据类型名与值都是存在于栈内存中、引用数据类型名存在与栈内存中,值存在于堆内存中,在栈内存对应的值是堆内存的地址值。
基本数据类型不存在深拷贝还是浅拷贝这一说法,此说法只存在于引用型数据。
深拷贝具体是指拷贝数据与被拷贝数据拷贝完成之后就完全没有关系了,拷贝数据发生变化不会影响被拷贝数据,浅拷贝则不是如此。
2.JS中确切来说并没有自带数据深拷贝方法
1.数组
如数组中的slice()、concat()等方法只能实现数组的一维深拷贝
const arr1 = ['a','b',['x','y']]
const arr2 = ['c','d']
const arr3 = arr1.concat(arr2)
// 一维数据是深拷贝,arr3数据与其他无关
arr3[0] = 'ww'
// 二维数据是浅拷贝,arr3数据与arr1有关
arr3[2][0] = 'pp'
console.log(arr1); // ['a','b',['pp','y']]
console.log(arr2); // ['c','d']
console.log(arr3); // ['ww','b',['pp','y'],'c','d']
2.对象
如对象中的Object.assign()也只能进行一维数据的深拷贝
const obj1 = {x:1,y:2}
const obj2 = Object.assign({},obj1)
console.log(obj1); // {x:1,y:2}
console.log(obj2); // {x:1,y:2}
obj2.x = 3 //修改一维数据
console.log(obj1); // {x:1,y:2} 数据不变
console.log(obj2); // {x:3,y:2} 数据变化 ***深拷贝
const obj3 = {a:{x:1,y:2}}
const obj4 = Object.assign(obj3)
console.log(obj3); // {a:{x:1,y:2}}
console.log(obj4); // {a:{x:1,y:2}}
obj4.a.x = 3 //修改二维数据
console.log(obj3); // {a:{x:3,y:2}} 数据变化
console.log(obj4); // {a:{x:3,y:2}} 数据变化 ***浅拷贝
3.JSON.parse(JSON.stringify(obj)) 可以进行多维深拷贝,但是会忽略function等数据
const obj1 = {
x:{a:1},
y:undefined,
z:function(){
console.log(123);
},
w:Symbol('pp')
}
const obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1); // {x:{a:1},u:undefined,w:Symbol(pp),z:f}
console.log(obj2); // {x:{a:1}} 忽略一部分值
obj2.x.a = 2
console.log(obj1); // {x:{a:1},u:undefined,w:Symbol(pp),z:f} 数据不变
console.log(obj2); // {x:{a:2}} 数据变化 ***部分深拷贝
二.手写深拷贝
1.递归实现:此实现方法当拷贝的数据很大时,可能会导致栈空间不够的情况,所以需要优化。
function deepClone(obj) {
// 对数组和对象进行区分
let result = Array.isArray(obj) ? [] : {};
// 如果obj的类型是对象,进入判断,如果不是,则代表是基本数据,直接赋值即可
if ( typeof obj === "object") {
for (let key in obj) {
// 如果二维及以上还是对象,则递归
if ( typeof obj[key] === "object") {
result[key] = deepClone(obj[key]);
} else {
result[key] = obj[key];
}
}
}
return result;
}
const obj1 = {
x: { a: 1 },
y: undefined,
z: function () {
console.log(123);
},
w: Symbol('pp')
}
const obj2 = deepClone(obj1)
obj2.x.a = 2
console.log(obj1); // {x:{a:1},u:undefined,w:Symbol(pp),z:f} 数据不变
console.log(obj2); // {x:{a:2},u:undefined,w:Symbol(pp),z:f} 数据变化 ***深拷贝
2.非递归
function deepClone(obj, parent = null){
let result; // 最后的返回结果
let _parent = parent; // 防止循环引用
while(_parent){
if(_parent.originalParent === obj){
return _parent.currentParent;
}
_parent = _parent.parent;
}
if(obj && typeof obj === "object"){ // 返回引用数据类型(null已被判断条件排除))
if(obj instanceof RegExp){ // RegExp类型
result = new RegExp(obj.source, obj.flags)
}else if(obj instanceof Date){ // Date类型
result = new Date(obj.getTime());
}else{
if(obj instanceof Array){ // Array类型
result = []
}else{ // Object类型,继承原型链
let proto = Object.getPrototypeOf(obj);
result = Object.create(proto);
}
for(let key in obj){ // Array类型 与 Object类型 的深拷贝
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key] === "object"){
result[key] = deepClone(obj[key],{
originalParent: obj,
currentParent: result,
parent: parent
});
}else{
result[key] = obj[key];
}
}
}
}
}else{ // 返回基本数据类型与Function类型,因为Function不需要深拷贝
return obj
}
return result;
}
// 调试用
function construct(){
this.a = 1,
this.b = {
x:2,
y:3,
z:[4,5,[6]]
},
this.c = [7,8,[9,10]],
this.d = new Date(),
this.e = /abc/ig,
this.f = function(a,b){
return a+b
},
this.g = null,
this.h = undefined,
this.i = "hello",
this.j = Symbol("foo")
}
construct.prototype.str = "I'm prototype"
var obj1 = new construct()
obj1.k = obj1
obj2 = deepClone(obj1)
obj2.b.x = 999
obj2.c[0] = 666
console.log(obj1)
console.log(obj2)
console.log(obj1.str)
console.log(obj2.str)
三.lodash插件深克隆
// 引入lodash
import cloneDeep from 'lodash/cloneDeep'
// 使用
this.attrInfo = cloneDeep(row)
lodash深克隆的数据变成了响应式数据(vue2)。