-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Open
Labels
Description
话不多说直接放代码
发现了比较多的错误,但由于最近工作有点忙,一直没来得及纠正
更改(0226)
// 工具函数
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) { // 判断环状数据
_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
}
测试
/**测试数据 */
// 输入 字符串String
// 预期输出String
let str = 'String'
var strCopy = DFSdeepClone(str)
var strCopy1 = BFSdeepClone(str)
console.log(strCopy, strCopy1) // String String 测试通过
// 输入 数字 -1980
// 预期输出数字 -1980
let num = -1980
var numCopy = DFSdeepClone(num)
var numCopy1 = BFSdeepClone(num)
console.log(numCopy, numCopy1) // -1980 -1980 测试通过
// 输入bool类型
// 预期输出bool类型
let bool = false
var boolCopy = DFSdeepClone(bool)
var boolCopy1 = BFSdeepClone(bool)
console.log(boolCopy, boolCopy1) //false false 测试通过
// 输入 null
// 预期输出 null
let nul = null
var nulCopy = DFSdeepClone(nul)
var nulCopy1 = BFSdeepClone(nul)
console.log(nulCopy, nulCopy1) //null null 测试通过
// 输入undefined
// 预期输出undefined
let und = undefined
var undCopy = DFSdeepClone(und)
var undCopy1 = BFSdeepClone(und)
console.log(undCopy, undCopy1) //undefined undefined 测试通过
//输入引用类型obj
let obj = {
a: 1,
b: () => console.log(1),
c: {
d: 3,
e: 4
},
f: [1, 2],
und: undefined,
nul: null
}
var objCopy = DFSdeepClone(obj)
var objCopy1 = BFSdeepClone(obj)
console.log(objCopy === objCopy1) // 对象类型判断 false 测试通过
console.log(obj.c === objCopy.c) // 对象类型判断 false 测试通过
console.log(obj.c === objCopy1.c) // 对象类型判断 false 测试通过
console.log(obj.b === objCopy1.b) // 函数类型判断 false 测试通过
console.log(obj.b === objCopy.b) // 函数类型判断 false 测试通过
console.log(obj.f === objCopy.f) // 数组类型判断 false 测试通过
console.log(obj.f === objCopy1.f) // 数组类型判断 false 测试通过
console.log(obj.nul, obj.und) // 输出null,undefined 测试通过
// 输入环状数据
// 预期不爆栈且深度复制
let circleObj = {
foo: {
name: function() {
console.log(1)
},
bar: {
name: 'bar',
baz: {
name: 'baz',
aChild: null //待会让它指向obj.foo
}
}
}
}
circleObj.foo.bar.baz.aChild = circleObj.foo
var circleObjCopy = DFSdeepClone(circleObj)
var circleObjCopy1 = BFSdeepClone(circleObj)
console.log(circleObjCopy, circleObjCopy1) // 测试通过?
ps
关于深浅拷贝的问题博主已经在他的git上有文章说过了,我就不做多的叙述
这两个方法我认为主要区别在于对于深层次以及环状数据,用深度优先遍历递归去做容易爆栈,广度优先遍历我对环状数据进行了处理,已经存在过的对象会存在数组中,下次直接赋值即可,无需继续遍历
如果出现问题欢迎讨论指出
ylinwind, Panzimon, GDLong, mannymu, roth1002 and 25 moreforgetallthing, Sberm and EsunRazl397985856, wqs2019, SuiKaSan, csm0912, mrandpz and 23 morealphaobj and williamLing19zbyechogqlicomeon, yaorihaohao, czw1314, xiaofeizao1996, alphaobj and 2 more
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
H246802 commentedon Feb 18, 2019
@atheist1 老哥你代码最好添加代码高亮一下,这样阅读感很差。
xiaobaichiliangpi commentedon Feb 19, 2019
笔误:
map = {
number: 'Number'
}
zhengjianghao commentedon Feb 21, 2019
DFSdeepClone 方法有问题吧 字符串 clone 出来不久变数组了
浏览器console 实验一下就错了
Tairraos commentedon Feb 22, 2019
数值赋值本身就是复制,clone的时候需要复制的是array, object和function,这个代码里并没有针对function成员的复制。
oychao commentedon Feb 25, 2019
DFS没有对环状结构进行处理~
atheist1 commentedon Feb 26, 2019
顺便说明下,这里我对es6的symbol数据以及构造函数的拷贝都没做处理,想要了解的可以看大佬的
这篇文章【进阶4-4期】Lodash是如何实现深拷贝的
LeeLejia commentedon Feb 26, 2019
DFSdeepClone = (obj, visitedArr = [])
,这里visitedArr如果传参的话,子层的属性不能和同层的属性做比较。只有同层的环能检测出来。let b = {haha: 'haha'} let a = { a: {b: b}, c: {b: b} } const rs = DFSdeepClone(a) console.log(rs.a.b === rs.c.b) // false console.log(a.a.b === a.c.b) // true
WozHuang commentedon Mar 2, 2019
我只深拷贝了 Object, Array,其他的非基本类型都是浅拷贝(如果处理Set什么的就太复杂了,题目用意应该是考察遍历树和重复引用吧)
DFS用常规的递归问题不大,需要注意下重复引用的问题,不用递归的话就用栈
BFS就用队列,整体代码倒是差不多
binsinger commentedon Mar 6, 2019
var o = {m: 2}
var obj = {
a: {b:o},
e: o
}
广度优先测试失败
atheist1 commentedon Mar 6, 2019
公司gayhub怎么老是炸,感谢提醒,已更新,在处理环状数据时我将
visitedCopyQueue
错用成visitedQueue
了,导致上一次使用的数据是存在visitedQueue
里的引用导致深复制失败zhw2590582 commentedon Mar 29, 2019
笔试时会不会纸上手写深度拷贝
Emensionyu commentedon Apr 7, 2019
代码有点难读
yanzhang146 commentedon Apr 11, 2019
[-]第六题 实现深度拷贝[/-][+]第 6 题:请分别用深度优先思想和广度优先思想实现一个拷贝函数?[/+]ibwei commentedon Jul 9, 2019
30 remaining items
hjiog commentedon Jul 11, 2020
zhaoyinpan2 commentedon Jul 15, 2020
function deepClone(source, hash = new WeakMap()) {
if (!isObject(source)) return source;
var constructor = source.constructor;
if (constructor == RegExp) {
return new constructor(source)
} else if (constructor == Date) {
return new constructor(source.getTime())
} else if (constructor == Function) {
return eval('('+source.toString()+')')
} else {
if (hash.has(source)) {
return hash.get(source)
} else {
var target = new constructor;
hash.set(source, target);
for (var key in source) {
target[key] = deepClone(source[key], hash)
}
}
}
return target
}
tjwyz commentedon Jul 16, 2020
高赞默写
zhouatie commentedon Jan 13, 2021
引用类型只考虑Array,Object
Yayure commentedon Feb 22, 2021
bosens-China commentedon Feb 26, 2021
补一个测试用例
··· js
var obj = {};
obj.target = obj;
Luz-Liu commentedon Mar 8, 2021
太强了, 这个对环状结构的处理才是正确的
wmb0412 commentedon Jun 8, 2021
HelloGGG commentedon Sep 25, 2021
对象深拷贝大家可能最先想到的就是DFS递归属性,递归实现相对简单代码就不贴了,这里放下BFS实现,拷贝主要需要注意下循环引用和重复引用问题,这个可以引入一个缓存数组解决。
`/**
*/
function deepClone(obj) {
// 边界处理
if (obj.proto !== Object.prototype && obj.proto !== Array.prototype) {
return obj;
}
// 结果对象
let res = {};
// 队列内放对应层级的属性的对象或数组
const queue = [[res, obj]];
// 记录已经创建的引用和对应的旧对象引用,处理循环引用和重复引用
const hasCloneAttr = [
{
pre: obj, // 旧对象引用
now: res, // 新对象引用
},
];
while (queue.length > 0) {
const length = queue.length;
for (let i = 0; i < length; i++) {
const v = queue.shift();
const keys = Object.keys(v[1]);
for (const key of keys) {
// 对数组和对象进行深拷贝
if (
v[1][key].proto === Object.prototype ||
v[1][key].proto === Array.prototype
) {
let flag = false;
for (let item of hasCloneAttr) {
// 旧对象存在相同引用,则不需要再创建新的引用,直接使用之前创建引用就行
if (item.pre === v[1][key]) {
v[0][key] = item.now;
flag = true;
break;
}
}
if (!flag) {
v[0][key] = v[1][key].proto === Object.prototype ? {} : [];
hasCloneAttr.push({
pre: v[1][key],
now: v[0][key],
});
// 引用类型放入队列,下次依次取出处理
queue.push([v[0][key], v[1][key]]);
}
}
// 基本类型,直接赋值
else {
v[0][key] = v[1][key];
}
}
}
}
return res;
}`
coding-nemo commentedon Jan 27, 2022
circleObj.foo.bar=1;
执行后发现 circleObjCopy 也被改了
ShopkeeperEgg commentedon Dec 24, 2024
只考虑基本数据类型和数组和对象。其他数据类型,例如map、set等可以在此基础上拓展
brother-forteen commentedon Dec 24, 2024