JS: 深拷贝
程序员文章站
2022-05-08 07:54:17
注意: 以下深拷贝仅针对对象。 对于深拷贝,我平时用得很少,一般都是用 JSON 的方法来实现: 但前几天踩了坑,在网上查了才发现问题,只能说坑只有踩过才知道深浅。 坑 1. 对于 function、undefined,会丢失这些属性。 2. 对于 RegExp、Error 对象,只会得到空对象 3 ......
注意:以下深拷贝仅针对对象。
对于深拷贝,我平时用得很少,一般都是用 json 的方法来实现:
let newobj = json.parse(json.stringify(oldobj))
但前几天踩了坑,在网上查了才发现问题,只能说坑只有踩过才知道深浅。
坑
对于 function、undefined,会丢失这些属性。
对于 regexp、error 对象,只会得到空对象
对于 date 对象,得到的结果是 string,而不是 date 对象
-
对于 nan、infinity、-infinity,会变成 null
let oldobj = { test1: null, test2: undefined, fn: () => {console.log('fn')}, date: new date(), regexp: /(a|b)/g, error: new error('err'), nan: number('nan') } let newobj = json.parse(json.stringify(oldobj)) // 丢失 function、undefined // error、regexp 为空对象 // nan 变为 null // date 对象变为 string newobj /* { error: {} regexp: {} date: "2019-04-16t11:43:05.870z" nan: null test1: null } */
-
无法处理循环引用
let oldobj = { } oldobj.obj = oldobj // 会报错 let newobj = json.parse(json.stringify(oldobj)) // typeerror: converting circular structure to json
浅拷贝
浅拷贝方法还是挺多的,列举一二:
-
object.assign()
let oldobj = { name: 'parent', children: 'children' } let newobj = object.assign({}, oldobj)
-
循环
/* * 浅拷贝,仅针对对象 * params {object} obj */ function shallowcopy (obj) { if (object.prototype.tostring.call(obj) !== '[object object]') { throw new typeerror(`${obj} is not a object`) } let res = {} for (let key in obj) { if (obj.hasownproperty(key)) { res[key] = obj[key] } } return res }
深拷贝
深拷贝的实现也是可以使用for...in + 递归实现的:
function isobject (obj) { return object.prototype.tostring.call(obj) === '[object object]' } /* * 深拷贝,仅针对对象 * params {object} obj */ function deepcopy (obj) { if (!isobject(obj)) { throw new typeerror(`${obj} is not a object`) } let res = {} for (let key in obj) { if (obj.hasownproperty(key)) { res[key] = isobject(obj[key]) ? deepcopy(obj[key]) : obj[key] } } return res }
虽然解决了大部分json.parse(json.stringify(oldobj))
的问题,但依然无法解决循环引用的问题。
let oldobj = {} oldobj.obj = oldobj let newobj = deepcopy(oldobj) newobj.obj === oldobj.obj // true
解决循环引用
其实只要将已被拷贝的对象存储下来,每次递归之前都检查一遍该对象是否已经被拷贝,就可以解决循环引用的问题了。
/* * 深拷贝,仅针对对象 * params {object} obj */ function deepcopy (obj, list = new weakmap()) { if (list.has(obj)) { return list.get(obj) } if (!isobject(obj)) { throw new typeerror(`${obj} is not a object`) } let res = {} list.set(obj, res) for (let key in obj) { if (obj.hasownproperty(key)) { res[key] = isobject(obj[key]) ? deepcopy(obj[key], list) : obj[key] } } return res }
这样循环引用的问题就解决啦,是不是很简单。
let oldobj = {} oldobj.obj = oldobj let newobj = deepcopy(oldobj) newobj.obj === oldobj.obj // false
当然,weakmap 可能会存在兼容性问题,所以可以将 list 改成数组。
上一篇: 贫穷让我们想象力丰富
下一篇: 这位经理,为何放弃治疗