<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var obj = {
a: {
b: 1,
c: 2
},
d: {
e: 3,
f: {
g: 4,
h: 45
}
}
}
// 为什么要用hasOwnproperty判断一层 深拷贝不需要赋值对象原型链上继承的属性
// 所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
// 该方法还有许多问题 如:typeof 后面详解
let deepClone = obj => {
var target = {};
for(var i in obj){
if(obj.hasOwnProperty(i)){
if(typeof obj[i] === 'object'){
target[i] = deepClone(obj[i]);
} else {
target[i] = obj[i];
}
}
}
return target;
}
// 上述方法有很多问题问题
/**
* 没有对参数做校验 如果不是对象直接return
* 判断对象的方法不严谨 Object.prototype.toString.call(obj, '[object Object]')
* 没有考虑数组的兼容
* 当clone很深的话 爆栈
* 循环引用 var a = {}; a.a = a; deepClone(a) 直接死循环 循环检测
*/
// 创建一个对象
function createData(deep, breadth) {
var data = {};
var temp = data;
for (var i = 0; i < deep; i++) {
temp = temp['data'] = {};
for (var j = 0; j < breadth; j++) {
temp[j] = j;
}
}
return data;
}
// createData(1, 3); // 1层深度,每层有3个数据 {data: {0: 0, 1: 1, 2: 2}}
// createData(3, 0); // 3层深度,每层有0个数据 {data: {data: {data: {}}}}
// 破解递归爆栈
/**
* 第一种是消除尾递归
* 循环 把对象看成一颗树
*/
// 用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点
// 首先我们往栈里放入种子数据,key用来存储放哪一个父元素的那一个子元素拷贝对象
// 然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝
// let newObj = deepClone(createData(10000)); // 爆栈: Uncaught RangeError: Maximum call stack size exceeded
// let newObj = deepClone(createData(10, 100000)); 不会报错 广度不会爆栈
// console.log(createData(10, 10));
let deepCloneUpgrade = obj => {
let target = {};
let stack = [];
if(Object.prototype.toString.call(obj) === '[object Object]'){
stack.push({
parent: target,
key: undefined,
data: obj
});
while(stack.length){
console.log(stack)
let item = stack.pop();
const parent = item.parent;
const key = item.key;
const data = item.data;
// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
let res = parent;
if(typeof key != 'undefined'){
// 浅拷贝 parent[key] 和 res指向同一块内存地址 一直是 target[data] 、target[data][data]
res = parent[key] = {};
}
for(let i in data){
if(data.hasOwnProperty(i)){
if(typeof data[i] === 'object'){
stack.push({
parent: res,
key: i,
data: data[i]
})
}
else {
res[i] = data[i];
}
}
}
// console.log(target)
}
}
return target;
}
// let newObj = deepCloneUpgrade(createData(2, 2));
// console.log(newObj);
var b = {};
var a = {
a1: b,
a2: b
}
console.log(a.a1 === a.a2)
// 循环深度深拷贝
// 解决爆栈和循环引用的问题
var deepClone = obj => {
const uniqueList = [];
const target = {};
const stack = [];
stack.push({
parent: target,
key: undefined,
data: obj
})
while (stack.length) {
let item = stack.pop();
let parent = item.parent;
let key = item.key;
let data = item.data;
let res = parent;
if (typeof key != 'undefined') {
res = parent[key] = {};
}
let uniqueData = find(uniqueList, data);
if (uniqueData) {
parent[key] = uniqueData.target;
continue;
}
uniqueList.push({
source: data,
target: res
})
for (let i in data) {
if (data.hasOwnProperty(i)) {
if (typeof data[i] == 'object') {
stack.push({
parent: res,
key: i,
data: data[i]
});
} else {
res[i] = data[i];
}
}
}
}
return target;
}
function find(arr, item) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].source === item) {
return arr[i];
}
}
return null;
}
var b = {};
var a = { a1: b, a2: b };
var c = deepClone(a);
console.log(a.a1 === a.a2)
console.log(c.a1 === c.a2)
</script>
</body>
</html>