假设要深拷贝以下数据 data
let data = {
a: 1
};
data.f=data
执行 deepClone(data)
,会发现控制台报错,错误信息如下所示。
这是因为递归进入死循环导致栈内存溢出了。根本原因是 data
数据存在循环引用,即对象的属性间接或直接的引用了自身。
function deepClone(target) {
function clone(target, map) {
if (target !== null && typeof target === ‘object’) {
let result = Object.prototype.toString.call(target) === “[object Array]” ? [] : {};
if (map[target]) {
return map[target];
}
map[target] = result;
for (let k in target) {
if (target.hasOwnProperty(k)) {
result[k] = deepClone(target[k])
}
}
return result;
} else {
return target;
}
}
let map = {}
const result = clone(target, map);
map = null;
return result
}
以上代码中利用额外的变量 map
来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去 map
中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。最后需要把变量 map
置为 null
,释放内存,防止内存泄露。
在这里可以向面试官展示你的两个编程能力。
-
对循环引用的理解,如何解决循环引用引起的问题的能力。
-
对内存泄露的认识和避免泄露的能力。
该段位要考虑性能问题了。在上面的代码中,我们遍历数组和对象都使用了 for...in
这种方式,实际上 for...in
在遍历时效率是非常低的,故用效率比较高的 while
来遍历。
function deepClone(target) {
/**
-
遍历数据处理函数
-
@array 要处理的数据
-
@callback 回调函数,接收两个参数 value 每一项的值 index 每一项的下标或者key。
*/
function handleWhile(array, callback) {
const length = array.length;
let index = -1;
while (++index < length) {
callback(array[index], index)
}
}</