前言
在前一篇已经介绍过了深度优先和广度优先
现在想通过这两个去实现一个深拷贝
深拷贝需要注意一些问题:环状数据
先了解过什么是环状数据后,再来进行深拷贝的实现
环状数据
相信大多数人都是第一次听说这个词,其实挺好理解的,先上代码看一看
const obj = {
foo: {
name: 'foo',
bar: {
name: 'bar'
baz: {
name: 'baz',
aChild: null //待会让它指向obj.foo
}
}
}
}
obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo 形成环
JSON.stringify(obj) // => TypeError: Converting circular structure to JSON
可以看到,子节点属性赋值了父节点的引用,形成了一个foo->bar->baz->aChild->foo这样封闭的环状数据结构,这样的操作会导致死循环
因此,深拷贝时,肯定要判断对象是否是环状数据,判断的方法其实就是保存每一个属性在数组中,每次都判断下一个进数组的属性是否在数组中已经存在,如果存在,那么就是环状数据了
这里可以写个demo
function cycleDetector(obj) {
var hasCircle = false, // 定义一个变量,标志是否有环
cache = []; // 定义一个数组,来保存对象类型的属性值
(function(obj) {
var keys = Object.keys(obj); //获取当前对象的属性数组
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = obj[key];
if (typeof value == 'object' && value !== null) {
var index = cache.indexOf(value)
if (~index) { // 判断环状数据
hasCircle = true
break
} else {
cache.push(value)
arguments.callee(value)
cache.pop() // 注意:这里要推出数据,因为递归返回,后面遍历的属性不是这个数据的子属性
}
}
}
})(obj)
return hasCircle
}
工具函数
// 工具函数
let _toString = Object.prototype.toString
let map = {
array: 'Array',
object: 'Object',
function: 'Function',
string: 'String',
null: 'Null',
undefined: 'Undefined',
boolean: 'Boolean',
number: 'Number'
}
let getType = (item) => {
return _toString.call(item).slice(8, -1)
}
let isTypeOf = (item, type) => {
return map[type] && map[type] === getType(item)
}
深度优先的深拷贝
let DFSdeepClone = (obj, visitedArr = []) => {
let _obj = {}
if (isTypeOf(obj, 'array') || isTypeOf(obj, 'object')) {
let index = visitedArr.indexOf(obj)
_obj = isTypeOf(obj, 'array') ? [] : {}
if (~index) { // 判断环状数据 ~-1 == 0 一个骚操作而已
_obj = visitedArr[index]
} else {
visitedArr.push(obj)
for (let item in obj) {
_obj[item] = DFSdeepClone(obj[item], visitedArr)
}
}
} else if (isTypeOf(obj, 'function')) {
_obj = eval('(' + obj.toString() + ')');
} else {
_obj = obj
}
return _obj
}
广度优先的拷贝
let BFSdeepClone = (obj) => {
let origin = [obj],
copyObj = {},
copy = [copyObj]
// 去除环状数据
let visitedQueue = [],
visitedCopyQueue = []
while (origin.length > 0) {
let items = origin.shift(),
_obj = copy.shift()
visitedQueue.push(items)
if (isTypeOf(items, 'object') || isTypeOf(items, 'array')) {
for (let item in items) {
let val = items[item]
if (isTypeOf(val, 'object')) {
let index = visitedQueue.indexOf(val)
if (!~index) {
_obj[item] = {}
//下次while循环使用给空对象提供数据
origin.push(val)
// 推入引用对象
copy.push(_obj[item])
} else {
_obj[item] = visitedCopyQueue[index]
visitedQueue.push(_obj)
}
} else if (isTypeOf(val, 'array')) {
// 数组类型在这里创建了一个空数组
_obj[item] = []
origin.push(val)
copy.push(_obj[item])
} else if (isTypeOf(val, 'function')) {
_obj[item] = eval('(' + val.toString() + ')');
} else {
_obj[item] = val
}
}
// 将已经处理过的对象数据推入数组 给环状数据使用
visitedCopyQueue.push(_obj)
} else if (isTypeOf(items, 'function')) {
copyObj = eval('(' + items.toString() + ')');
} else {
copyObj = obj
}
}
return copyObj
}