let obj1 = {
name:'cdd'
curse:{
c1:'数学',
c2:'语文'
}
}
//let obj2 = obj1// 不是克隆,这只是让obj1/obj2共用一个堆内存,所谓克隆最起码是赋值一份一模一样的,是一个全新的堆内存。
console.log(obj1)
浅克隆
浅克隆:只克隆了第一级,对于后代级别还是公用之前的,新克隆的对象/数组还是可以改变原对象/数组中的内容
对象
- 循环遍历
let obj2 = {}
keys = [
...Object.keys(obj1)
...Object.getOwnPropertySymbols(obj1)
]
keys.forEach(key => {
obj2[key] = obj1[key]
})
console.log(obj2==obj1)//false
console.log(obj2.course==obj1.course)//true
- 基于展开运算符
let obj2 = {
...obj1
}
console.log(obj1==obj2)//false
console.log(obj1.course==obj2.course)//true
- 基于Object.assign
//Object.assign([obj1],[bj2]);返回的结果依然是obj1堆内存,只不过是把obj2中的键值对和原始obj1的键值对合并在一起了
//let obj2 = Object.assign(obj1,{})//obj2==obj1
let obj2 = Object.assign({},obj1)
console.log(obj2==obj1)//false
console.log(obj2.course==obj.course)//true
数组
- 循环遍历
let arr1 = [10,20,[30,40]]
let arr2 = []
arr1.forEach((item,index) => {
arr2[index] = item
}
//使用map...
arr2 = arr1.map(item => item)
console.log(arr1 === arr2)//false
console.log(arr1[2] === arr2[2])//true
- 基于展开运算符或者Object.assign都可以实现
- 基于slice/concat…方法
let arr2 = arr1.slice()
console.log(arr2 === arr1)//false
console.log(arr2[2] === arr[2])//true
深克隆
深克隆:当前级及其所有的后代级别,最后都会克隆一份一模一样的,和原始的数据结构不存在任何的关联
,会消耗内存空间
- 基于JSON.stringify/JSON.parse
//基于JSON.stringify,把原始对象(或者数组)变为一个字符串
// 再基于JSON.parse,把字符串转换为对象,此时对象对应每个级别的堆内存都是全新开辟的
let obj2 = JSON.parse(JSON.stringify(obj1))
let arr2 = JSON.parse(JSON.stringify(arr1))
console.log(obj2 === obj1)//false
console.log(obj1.course === obj2.course)//false
console.log(arr1 === arr2)//false
console.log(arr1[2] === arr2[2])//false
基于JSON的方法实现深克隆,存在的问题:「因为JSON.stringify变为字符串,很多类型是不支持的」
- 正则/Math对象/ArrayBuffer会被处理为空对象
- 具备函数/Symbol/undefined属性值的属性直接被干掉了
- BigInt还处理不了,会报错Uncaught TypeError: Do not know how to seriaize a BigInt.
- 日期对象最后还是字符串
- …
实现浅克隆
// 获取所有的私有属性,包含Symbol私有属性
function getOwnPropertys(obj) {
if (obj == null) return []
return [
...Object.keys(obj)
...Object.getOwnPrpertySymbols(obj)
]
}
// 浅克隆
function shallowClone (obj) {
// 处理其它类型的值克隆
let type =toType(obj)
if(/^(number|string|boolean|null|undfined|symbol|bigint)$/.test(type)) return obj
if(/^function$/.test(type)){
//返回一个不同的函数,但是最后执行的效果和原始函数一致
return function proxy(){
return obj()
}
}
if(/^(regexp|date)$/.test(type)) return new
obj.constructor(obj)
if(/^error$/.test(type)) return obj.constructor(obj.message)
//...
//只处理数组(最后返回的是数组)和对象(普通对象/类数组对象等都返回普通对象)
let keys = getOwnPropertys(obj),
clone = {}
Array.isArray(obj) ? clone = [] : null
keys.forEach((item,index) => {
clone[item] = obj[item]
})
return clone
}
实现深克隆
// 检测数据类型的方法封装
(function () {
var class2type = {};
var toString = class2type.toString;
[
"Boolean",
"Number",
"String",
"Symbol",
"Function",
"Array",
"Date",
"RegExp",
"Object",
"Error"
].forEach(function (name) {
class2type["[object " + name + "]"] = name.toLowerCase();
});
function toType(obj) {
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[toString.call(obj)] || "object" :
typeof obj;
}
window.toType = toType;
})();
function deepClone(obj,cache = new Set()) {
// 只有数组和对象,我们再处理深克隆,其余的情况直接按照浅克隆处理即可
let type = toType(obj)
if(/^(object|array)$/.test(type)) return shallowClone(obj)
// 避免自己套用自己导致的无限递归
if (cache.has(obj)) return shallowClone(obj)
cache.add(obj)
let keys = getOwnPropertys(obj),
clone = {}
type === 'array' ? clone = [] : null
keys.forEach(key => {
clone[key] = deepClone(obj[key],cache)
})
return clone
}