1.什么是浅拷贝,什么是深拷贝?
A: (结合内存模型回答,显得专业)
它们的核心区别在于对引用类型值的处理方式不同 [[3, 6, 7:
- 浅拷贝: 只复制对象的第一层属性。对于基本数据类型,直接赋值;但对于引用类型(如对象、数组),它复制的是内存地址(引用) 。这意味着,新旧对象的引用类型属性会共享同一块内存,修改其中一个,另一个也会受到影响。
- 深拷贝: 递归复制对象的所有层级。它会开辟全新的内存空间,存放一份完全独立的副本。新旧对象完全不共享引用,修改其中一个,绝对不会影响另一个。
2.JavaScript 中有哪些实现深拷贝和浅拷贝的方法?
浅拷贝的实现方式
- 展开运算符 (
...): 最常用,语法简洁。 Object.assign(): ES6 标准方法。- 数组方法:
slice()、concat()、map()、filter()(仅限数组)。 -
深拷贝的实现方式
JSON.parse(JSON.stringify(obj)): 最简单粗暴的方法,但有局限性(面试必考坑点)。
- 手写递归: 最能体现原理,但需要处理循环引用。
- 第三方库: 如 Lodash 的
_.cloneDeep(),生产环境推荐使用。 structuredClone(): 浏览器原生支持的深拷贝 API(较新的考点)
Q: 使用 JSON.parse(JSON.stringify(obj)) 实现深拷贝有什么缺点?
A: (这是面试官最爱听的细节,一定要多说)
虽然这种方法简单,但它无法处理很多特殊的 JavaScript 对象和类型,主要缺点如下
-
丢失
undefined和Symbol: 这两种类型的属性会被直接忽略。 -
无法处理函数: 函数会被忽略,导致属性丢失。
-
无法处理循环引用: 如果对象内部引用了自己(
obj.a = obj),该方法会直接报错TypeError。 -
特殊对象变形:
Date对象会被转换成字符串。RegExp、Error对象会变成空对象{}。Map、Set、WeakMap、WeakSet等数据结构会变为空对象或丢失。
-
NaN、Infinity: 会被转换为null。
Q: 请手写一个深拷贝函数,要求能处理循环引用。
A: (这是进阶加分项,建议写出带 WeakMap 的优化版)
这个问题通常分两步考察:
function deepClone(target, cache = new WeakMap()) {
// 1. 基本类型或 null 直接返回
if (typeof target !== "object" || target === null) {
return target;
}
// 2. 解决循环引用 (核心逻辑)
if (cache.has(target)) {
return cache.get(target); // 如果已缓存,直接返回缓存结果
}
// 3. 区分数组和对象,创建容器
let cloneTarget = Array.isArray(target) ? [] : {};
cache.set(target, cloneTarget); // 将当前对象存入缓存
// 4. 递归拷贝 (这里为了简洁用了 for...in,实际可考虑 Reflect.ownKeys 处理 Symbol)
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone(target[key], cache);
}
}
return cloneTarget;
}
// 测试循环引用
const obj = { name: "Alice" };
obj.self = obj;
const copy = deepClone(obj);
console.log(copy.self === copy); // true,且不会爆栈
为什么要用 WeakMap?
Map对键是强引用,即使外部对象销毁了,Map 里的引用还在,会导致内存泄漏。WeakMap对键是弱引用,当外部对象销毁时,垃圾回收器可以回收它,不会造成内存泄漏
🤔 5. 场景应用题:如何选择?
Q: 在实际开发中,你会如何选择使用深拷贝还是浅拷贝?
A:
-
优先使用浅拷贝: 在 React/Vue 的状态管理中,大多数情况下(如更新对象的一层属性)使用浅拷贝(展开运算符)即可,性能更好,也符合不可变数据的原则。
-
使用深拷贝:
- 当你需要对数据进行完全隔离的操作,且不希望影响原数据时(例如:数据快照、撤销/重做功能)。
- 当对象结构极其复杂且包含大量嵌套时。
-
生产环境建议: 除非数据结构非常简单,否则不建议自己造轮子,推荐使用成熟的工具库(如 Lodash)或原生
structuredClone,因为它们处理了大量边界情况