大家好,深拷贝在面试中屡见不鲜, 今天试试简单的深拷贝和大家分享一波~~
首先, 常见的拷贝方法如下, 但是各有利弊:
Object.assign( target, source) // target 是目标对象, source是数据源
JSON.parse(JSON.stringify()) // 序列化 ,反序列化实现
建议:可以学习 loash 中的拷贝方法, 是一个比较全面的克隆处理解决方案, 此处不做说明; 因为本菜鸟还没研究过,后续研究研究, 哈哈~~
第一:Object.assign( target, source)
只能克隆第一层, 第二层克隆了引用地址, 无法实现深度克隆。 个人理解, 其实严格意义可以理解为浅拷贝
let obj = { name: '美妞', testObj : { a: 12 }, list: ['string', 123] } let newObj ={} Object.assign(newObj, obj) newObj.list[1] = 100 newObj.testObj.a = 200 console.log("obj", obj) console.log("newObj", newObj)
打印结果为: 对克隆后数据进行修改,源数据也会被改掉, 所以, Object .assing() 是无法实现深拷贝的
第二: JSON.parse(JSON.stringify())
其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;序列化的作用是存储(对象本身存储的只是一个地址映射,如果断电,对象将不复存在,因此需将对象的内容转换成字符串的形式再保存在磁盘上 )和传输。
不过,这种实现深拷贝的方法有局限性, 它只适用于一般的数据拷贝(对象, 数组), 有些需要注意:
(1) 如果json里面有时间对象, 则序列化结果: 时间对象=>字符串形式
let source = {
name: '美妞',
date: new Date()
}
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
console.log(typeof source.date) // object
console.log(typeof target.date) // string
(2)如果Json里边有RegExp, Error对象, 则序列化的结果将只得到空的对象RegExp, Error ={}
let source = {
name: '美妞',
reg: new RegExp('\\w+'),
err: new Error('error message')
}
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
(3)如果对象中存在循环引用的情况, 无法实现深拷贝
let source = {
name: '美妞',
}
source.source = source
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
(4) 如果json中有undefined , function。 则序列化的结果会把function, undefined丢失
let source = {
name: '美妞',
fn:function () {
console.log('fn')
},
aa: undefined
}
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
(5)如果json里面有NaN, infinity 和 -infinity, 则序列化的结果会变成null
let source = {
name: '美妞',
aa: NaN,
isInfinite: 1.7976931348623157E+10308,
minusInfinity: -1.7976931348623157E+10308
}
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
(6)如果json里面有对象是由构造函数生成的, 则序列话的结果会丢弃对象的constructor
function Student(name) {
this.name = name;
}
let source = {
name: '美妞',
s: new Student('活宝')
}
let target = JSON.parse(JSON.stringify(source))
console.log('source', source)
console.log('target', target)
以下是手写实现一个深拷贝
// 1. 判断类型 2. 递归
let isType = (obj, type) => {
if (typeof obj !== 'object') return false;
let typeString = Object.prototype.toString.call(obj)
let flag;
switch (type) {
case "Array":
flag = typeString == "[object Array]";
break;
default:
flag = false;
}
return flag;
}
// source 原对象 target 克隆后的对象
let argS = []; let argT = [];
let deepClone = source => {
if (typeof source === null) return null;
if (typeof source !== 'object') return source;
let target, proto;
if (isType(source, "Array")){
target = []
} else {
// 拿到source原型对象
proto = Object.getPrototypeOf(source);
target = Object.create(proto);
}
// 判断是是否克隆结束
let index = argS.indexOf(source);
if (index !== -1) {
return target[index]
}
argS.push(source);
argT.push(target);
// 对象内部循环处理
for (let i in source) {
target[i] = deepClone(source[i]);
}
return target;
}
let sourceData = {
a: '5555',
b: {
name: 'test'
},
c: [2,3,4],
d: function () {
console.log(222)
}
}
let targetData = deepClone(sourceData);
targetData.b.name = 'test--大'
targetData.c[0] = 100
targetData.d =function () {
console.log(33333)
}
// sourceData.d()
// targetData.d()
// console.log(Object.prototype.toString.call(sourceData.c))
// console.log(Object.prototype.toString.call(targetData.c))
console.log("sourceData", sourceData)
console.log("targetData", targetData)
实现方式比较啰嗦, 但是可以解决简单的拷贝。。。