当前位置: 首页 > web >正文

JavaScript面试题之深浅拷贝

JavaScript深拷贝与浅拷贝深度解析

一、内存存储基础原理

1. 栈内存与堆内存

  • 栈内存:存储基本类型值(Number、String、Boolean、Null、Undefined、Symbol、BigInt)
  • 堆内存:存储引用类型值(Object、Array、Function、Date等)
let a = 10;        // 栈内存存储
let b = { x: 20 }; // 堆内存存储,栈存引用地址

2. 赋值操作本质

let obj1 = { a: 1 };
let obj2 = obj1;    // 复制引用地址
obj2.a = 2;
console.log(obj1.a); // 输出2(原始对象被修改)

二、浅拷贝(Shallow Copy)

1. 核心特征

  • 创建新对象
  • 复制原始对象的属性值
  • 对引用类型属性复制指针(共享内存地址)

2. 常见实现方式

方法1:Object.assign()
const source = { a: 1,b: { c: 2 }
};
const shallowCopy = Object.assign({}, source);shallowCopy.b.c = 3;
console.log(source.b.c); // 输出3(原始对象被修改)
方法2:展开运算符
const arr = [1, [2, 3], { x: 4 }];
const arrCopy = [...arr];arrCopy[1][0] = 99;
console.log(arr[1][0]); // 输出99(原始数组被修改)
方法3:数组方法
const original = [{x:1}, 2, 3];
const sliceCopy = original.slice();
const mapCopy = original.map(item => item);

3. 应用场景

  • 简单对象结构拷贝
  • 需要保留对象引用关系时
  • 性能敏感场景(数据量较大时)

三、深拷贝(Deep Copy)

1. 核心特征

  • 创建完全独立的新对象
  • 递归复制所有嵌套属性
  • 完全脱离原始对象内存空间

2. 实现方式对比

方法1:JSON序列化(最简单方案)
const obj = { date: new Date(),fn: function() {},regexp: /pattern/ 
};const jsonCopy = JSON.parse(JSON.stringify(obj));
// 丢失信息:date转为字符串,函数被过滤,正则表达式变为空对象

局限性

  • 无法处理函数、undefined、Symbol
  • 忽略原型链
  • 循环引用报错
  • 特殊对象(Date/RegExp等)类型丢失
方法2:递归实现(基础版)
function deepClone(target) {if (typeof target !== 'object' || target === null) {return target;}const clone = Array.isArray(target) ? [] : {};for (const key in target) {if (target.hasOwnProperty(key)) {clone[key] = deepClone(target[key]);}}return clone;
}
方法3:结构化克隆算法(现代方案)
// 浏览器环境
const structuredCopy = structuredClone(obj);// Node.js环境
const { structuredClone } = require('util');

支持类型

  • 原始类型
  • Array/ArrayBuffer
  • Map/Set
  • Date/RegExp
  • Error类型
  • Blob/File/FileList
  • ImageData
  • 循环引用

不支持类型

  • 函数
  • DOM节点
  • 属性描述符/getter/setter
  • 对象原型链

3. 高级实现方案

完整深拷贝函数实现
function deepClone(target, map = new WeakMap()) {// 处理原始类型if (typeof target !== 'object' || target === null) {return target;}// 处理循环引用if (map.has(target)) {return map.get(target);}// 处理特殊对象const constructor = target.constructor;switch (constructor) {case RegExp:return new RegExp(target);case Date:return new Date(target);case Map:return new Map(Array.from(target, ([k, v]) => [k, deepClone(v, map)]));case Set:return new Set(Array.from(target, v => deepClone(v, map)));// 可扩展其他类型}// 初始化克隆对象const clone = new constructor();map.set(target, clone);// 处理Symbol属性const symbols = Object.getOwnPropertySymbols(target);const allKeys = [...Object.keys(target), ...symbols];// 递归拷贝for (const key of allKeys) {clone[key] = deepClone(target[key], map);}// 保留原型链Object.setPrototypeOf(clone, Object.getPrototypeOf(target));return clone;
}

4. 特殊场景处理

循环引用处理
const obj = { a: 1 };
obj.self = obj;const cloned = deepClone(obj); // 正确处理循环引用
console.log(cloned.self === cloned); // true
函数拷贝处理
function cloneFunction(fn) {return new Function('return ' + fn.toString())();
}const originalFunc = () => console.log('Hello');
const clonedFunc = cloneFunction(originalFunc);

四、性能优化策略

1. 数据规模影响

方法10^3属性10^5属性嵌套层级10
JSON方法1.2ms120ms失败
递归深拷贝2.5ms300ms栈溢出
lodash.cloneDeep1.8ms150ms正常

2. 优化技巧

  • 使用迭代代替递归(避免栈溢出)
  • 对象缓存池复用
  • 使用位运算判断类型
  • 并行化处理(Web Worker)

五、应用场景分析

1. 必须使用深拷贝

  • 状态管理(Redux/Vuex)
  • 配置对象复用
  • 历史记录功能
  • 数据快照保存

2. 推荐使用浅拷贝

  • 不可变数据更新
  • 组件props传递
  • 函数参数传递
  • 临时对象处理

六、三方库实现对比

1. lodash.cloneDeep

import cloneDeep from 'lodash/cloneDeep';const cloned = cloneDeep(original);

特点

  • 支持超过50种类型
  • 处理循环引用
  • 保留类型信息
  • 性能优化好

2. Immutable.js

import { fromJS } from 'immutable';const original = { a: 1, b: { c: 2 } };
const immutableCopy = fromJS(original).toJS();

优势

  • 结构共享优化内存
  • 高效的不可变数据操作
  • 内置类型支持

七、最佳实践指南

  1. 数据冻结检测
function deepFreeze(obj) {Object.freeze(obj);Object.keys(obj).forEach(key => {if (typeof obj[key] === 'object' && !Object.isFrozen(obj[key])) {deepFreeze(obj[key]);}});return obj;
}
  1. 拷贝验证方法
function isDeepEqual(a, b) {if (a === b) return true;if (typeof a !== typeof b) return false;if (typeof a !== 'object') return a === b;const keysA = Object.keys(a);const keysB = Object.keys(b);if (keysA.length !== keysB.length) return false;return keysA.every(key => isDeepEqual(a[key], b[key]));
}
  1. 生产环境建议
  • 优先使用结构化克隆API
  • 复杂场景选择lodash.cloneDeep
  • 大数据量考虑Immutable.js
  • 避免在循环中执行深拷贝

八、常见误区解析

1. 深拷贝与不可变对象

  • 深拷贝创建新对象
  • 不可变对象禁止修改
  • 两者可结合使用

2. 原型链处理

class MyClass {}
const original = new MyClass();
const cloned = Object.assign(Object.create(Object.getPrototypeOf(original)), original
);

3. 特殊属性处理

const obj = {get computed() { return this.x * 2 },set computed(val) { this.x = val / 2 }
};// 解决方案
const clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)
);

九、浏览器兼容性

方法/特性ChromeFirefoxSafariNode.js
structuredClone98+94+15.4+17+
lodash.cloneDeep全支持全支持全支持全支持
MessageChannel全支持全支持全支持全支持

总结

深拷贝与浅拷贝的本质区别在于对引用类型值的处理方式。实际开发中需要根据具体场景选择合适方案:

  • 简单数据操作优先使用浅拷贝
  • 复杂对象结构建议使用结构化克隆
  • 特殊类型处理推荐使用成熟工具库
  • 性能敏感场景考虑不可变数据结构

理解拷贝机制对于避免隐蔽的bug、优化内存使用、提升应用性能都具有重要意义。建议在项目中建立统一的拷贝策略,并通过代码审查确保内存操作的安全性。

http://www.xdnf.cn/news/9097.html

相关文章:

  • PBX、IP PBX、FXO 、FXS 、VOIP、SIP 的概念解析以及关系
  • SpringAI(GA):Tool工具整合—快速上手
  • [7-1] ADC模数转换器 江协科技学习笔记(14个知识点)
  • 开源软件协议大白话分类指南
  • [yolov11改进系列]基于yolov11引入自注意力与卷积混合模块ACmix提高FPS+检测效率python源码+训练源码
  • 常见算法题目4 - 给定一个字符串,判断是否为有效的括号
  • 鸿蒙桌面快捷方式开发
  • 进程通信(管道,共享内存实现)
  • 【unity游戏开发——编辑器扩展】Gizmos可视化辅助工具
  • Leetcode 1924. 安装栅栏 II
  • RabbitMQ 集群与高可用方案设计(二)
  • PyTorch实战(7)——生成对抗网络(Generative Adversarial Network, GAN)实践详解
  • 黑龙江云前沿-服务器托管
  • CentOS7安装 htop(100% 可以安上)
  • 使用VuePress开发日志
  • Redis与Lua脚本深度解析:原理、应用与最佳实践
  • ES文件管理器 安卓APP(文件管理器) v4.4.3.0 无广告高级版
  • 【无标题】第一章 Hello World的诅咒
  • 古腾堡编辑器教程:如何使用WordPress图库区块
  • 第十讲 | 继承
  • 商品颜色/尺码选项太多谷歌爬虫不收录怎么办?
  • 自动化测试:等待方式
  • 体育数据支撑比分网的全链路技术解析:从架构设计到场景落地
  • SQLMesh 用户定义变量详解:从全局到局部的全方位配置指南
  • OpenSSL 文件验签与字符串验签原理及 C 语言实现详解
  • 编程中优秀大模型推荐:特点与应用场景深度分析
  • Pycharm的简单介绍
  • 002大模型-提示词工程,少样本提示,角色扮演,思维链
  • 基于python+Django+Mysql的校园二手交易市场
  • 在 Windows 上使用 WSL 安装 Ansible详细步骤