浅谈JavaScript深拷贝
前言
JavaScript的浅拷贝、深拷贝是一个老生常谈的话题,真正完美的深拷贝其实是比较困难的,但相对的能应用的场景也同样比较少,个人感觉浅拷贝和深拷贝的核心概念无非是对JavaScript引用类型的理解,普通的值类型可以直接复制,应用类型的直接复制则是对其地址的复制,这个其实就是C语言中的指针的概念。明白了这些深浅拷贝就不难理解了,接下来分享一些常用的操作。
浅拷贝
浅拷贝就是不考虑引用类型,我们把对象的属性或是数组的元素都当作原始类型来看待。
Object.assign()
这个方法是用来合并两个对象的属性,将要拷贝的对象和一个空对象合并就能成为一个新的对象
cosnt temp = { a: 1, b: 2}
cosnt obj = Object.assign({}, temp)
temp === obj // false
es6扩展运算符 ...
cosnt temp = { a: 1, b: 2}
cosnt obj = { ...temp }
这种方法可以将temp里的属性提取出来放到新的对象里面
const array = [1,2,3,4,5]
cosnt newArray = [...array]
数组也是同理
深拷贝
深拷贝要比浅拷贝难实现的多,浅拷贝的方式会把引用类型的地址直接复制过去,不同的两个对象里面的引用类型的属性会互相影响,这种有时候不会符合我们的要求,此时就需要深拷贝。
JSON.parse(JSON.stringify())
这种配合使用算是比较常用的一种深拷贝方式了,将对象转成字符串,再重新生成对象,这样新生成的对象的引用类型和原对象里面的就不同了。
cosnt temp = { a: 1, b: 2, c: { d: 1 } }
cosnt obj = JSON.parse(JSON.stringif(temp))
temp === obj // false
temp.c === obj.c // false
这种方式的缺陷是undefined, 函数和对象内部循环引用的属性无法拷贝
MessageChannel
MessageChannel的介绍可以查看MDN,这个API其实是建立一个消息管道来进行页面通信使用的,使用这个API的比JSON的方法的优势在于可以进行有循环引用对象的拷贝,但是依然无法拷贝函数
function deepClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = event => {
return resolve(event.data)
}
port1.postMessage(obj)
})
}
const temp = { a: 1, b: 2, c: { d: 1 } }
deepClone(temp).then(obj => {
console.log(obj)
})
_.cloneDeep(value)
_.cloneDeep()是lodash库所提供的深拷贝函数,也是个人比较推荐的一种,基本可以满足深拷贝的要求,不需要我们再自己写工具类。
自定义工具类deeepClone
最后当然得自己实现一个,但是一个完美的深拷贝函数要考虑的东西太多了,比如要不要考虑原型链上的属性、Dom对象如何处理、函数如何处理等,所以我提供了一个不太完善的深拷贝函数,这个函数对于对象方法属性的深拷贝也是想了比较久才写出来的。
function deepClone(obj, parent) {
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function isFunction(obj) {
return typeof obj === 'function'
}
if(!isObj(obj)) {
return obj
}
if(isFunction(obj)) {
return obj.bind(parent)
}
const newObj = Array.isArray(obj) ? [...obj] : {...obj}
Reflect.ownKeys(obj).forEach(key => {
newObj[key] = isObj(newObj[key]) ? deepClone(newObj[key], newObj) : newObj[key]
})
return newObj
}
let temp = { a: 123, b: { c: function () {return 3} } }
let obj = deepClone(temp)
结语
每写一篇文章感觉都是对知识的总结提炼,虽然没人看也要坚持下去。
如果有疏漏的地方,欢迎指出
发表评论 (审核通过后显示评论):