这是我参与 8 月更文挑战的第 23 天,活动详情查看: 8月更文挑战
前言
这是一道非常经典的面试题,而且似乎每个前端都能答出点什么,比如我们可以这么问:
- 请问了解过浅拷贝和深拷贝吗?
- 能大概说一下深拷贝的实现思路吗?
- 能用代码实现一下深拷贝吗?
- 遇到一些特殊类型的时候该怎么处理?比如函数、日期
- 数据量非常大的时候,会有什么问题呢? ...
列举的问题都是循序渐进的,如果前面没答出来,就下一题吧。。。
请问了解过浅拷贝和深拷贝吗?
这题相当于送分题,直接公布答案:
- 浅拷贝只复制指向某个对象的指针,而不复制对象本身,相当于是新建了一个对象,该对象复制了原对象的指针,新旧对象还是共用一个内存块
- 深拷贝是新建一个一模一样的对象,该对象与原对象共享内存,修改新对象也不会影响原对象
简单来说,浅拷贝只拷贝第一层对象,相当于一个壳,里面套的还是旧对象,而深拷贝则是完成拷贝一份全新的对象。
能大概说一下深拷贝的实现思路吗?
想要实现深拷贝,我们必须得先弄明白浅拷贝,因为深拷贝是递归+拷贝的思路。
浅拷贝的实现方式
- Object.assign()
function cloneObj(obj) {
return Object.assign({},obj)
}
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
- Array.prototype.slice()
function cloneArr(arr) {
return arr.slice()
}
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
- Array.prototype.concat()
function cloneArr(arr) {
return arr.concat()
}
concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
- 解构赋值
function cloneObj(obj) {
return {...arr}
}
function cloneArr(arr) {
return [...arr]
}
能用代码实现一下深拷贝吗?
有了上面的基础,我们来实现一版本深拷贝
function deepClone(obj) {
if (Array.isArray(obj)) {
return obj.map(item => deepClone(deepClone))
}
if (typof obj === 'object' && obj !== null) {
const _obj = {}
for(var i in obj){
_obj[i] = deepClone(obj[i])
}
return _obj
}
return obj
}
小结
对于开篇提到的【数据量非常大的时候,会有什么问题呢?】,这是一个开放性的问题。一般该问题的方向如下:
- 递归会导致函数调用栈无法释放,最终爆栈
- 解决思路:1. 用数据模拟函数调用栈;2. 尾递归优化
- 执行时间过长
- 解决思路:1. 分析数据结构加入优化策略;2.分片执行;3.并发模式