面试题答案
一键面试难点
- 循环引用:如果对象存在循环引用,常规的深拷贝方法会陷入无限循环,因为在拷贝过程中会不断尝试拷贝同一个引用,导致栈溢出。
- 函数:JavaScript 函数不能简单地通过复制值来拷贝。函数具有作用域、闭包等特性,直接拷贝可能会导致函数执行环境错误。
- 日期对象:日期对象是 JavaScript 的内置对象,它不是简单的键值对结构,深拷贝时需要创建新的日期对象并设置相同的时间值。
- 其他特殊对象:如正则表达式对象、Error 对象等,这些对象有其特定的内部状态和行为,在深拷贝时需要特殊处理以保持其特性。
解决方法
- 使用 lodash:
- Lodash 的
_.cloneDeep
方法可以处理大部分复杂对象的深拷贝,包括循环引用。它内部维护了一个栈来跟踪已经处理过的对象,避免无限循环。
const _ = require('lodash'); const original = { /* 复杂对象 */ }; const copy = _.cloneDeep(original);
- Lodash 的
- 自行编写代码:
- 处理循环引用:可以使用一个 WeakMap(如果支持,它更适合这种场景,因为它不会阻止对象被垃圾回收)或普通对象来记录已经拷贝过的对象。
function deepClone(obj, map = new WeakMap()) { if (typeof obj!== 'object' || obj === null) { return obj; } if (map.has(obj)) { return map.get(obj); } let result; if (Array.isArray(obj)) { result = []; } else if (obj instanceof Date) { result = new Date(obj.getTime()); } else if (obj instanceof RegExp) { result = new RegExp(obj.source, obj.flags); } else { result = {}; } map.set(obj, result); for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { result[key] = deepClone(obj[key], map); } } return result; }
- 处理函数:对于函数,可以选择不拷贝函数,而是返回一个占位符(比如
null
或一个特殊的标识),或者根据需求重新定义函数。例如,在某些情况下可以将函数体提取出来,在新对象中重新定义相同的函数。 - 处理日期对象:如上述代码,创建一个新的日期对象并设置相同的时间值。对于其他特殊对象,类似地,根据其特性创建新的对象并设置相应的状态。