深拷贝中类型安全检测的核心是用object.prototype.tostring.call()精确识别所有内置类型,如null、array、date、regexp、map等,再分类型定制构造,避免降级或丢失状态,并配合weakmap处理循环引用。

深拷贝函数中做类型安全检测,核心是准确识别值的原始类型和引用类型,并对每种类型采取合适处理策略,避免误判(比如把 null 当作对象、把 Date 或 RegExp 当作普通对象)。
用 Object.prototype.toString.call() 精确判断类型
这是最可靠的方式,能区分 null、Array、Date、RegExp、Map、Set 等所有内置类型,不受原型链污染影响。
-
Object.prototype.toString.call(null)→"[object Null]" -
Object.prototype.toString.call([])→"[object Array]" -
Object.prototype.toString.call(new Date())→"[object Date]" -
Object.prototype.toString.call(/abc/)→"[object RegExp]" -
Object.prototype.toString.call(new Map())→"[object Map]"
区分基础类型与可递归类型
基础类型(string、number、boolean、undefined、null、symbol、bigint)直接返回副本;其余需递归或特殊构造。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
typeof x === 'object' || typeof x === 'function'是粗筛,但null也会命中,必须配合toString进一步确认 -
typeof x === 'function'可直接返回原函数(函数通常不深拷贝,或按需浅复制) -
typeof x === 'symbol'和typeof x === 'bigint'需显式处理,它们不是对象但不能用JSON.stringify
对特殊内置类型做定制化拷贝
不能统一用 Object.assign 或遍历键名,否则会丢失内部状态(如 Date 的时间戳、RegExp 的标志和模式)。
-
Date:用new Date(obj.getTime())构造新实例 -
RegExp:用new RegExp(obj.source, obj.flags)(注意flags在旧版本可能不可枚举,建议用obj.toString().match(/\/.*\/([gimyus]*)/)兜底) -
Map/Set:先新建实例,再遍历插入键值或元素 -
ArrayBuffer/TypedArray:用slice(0)或new ctor(obj.buffer, obj.byteOffset, obj.length)
避免循环引用导致栈溢出
类型检测本身不解决循环引用,但深拷贝中必须配合弱映射(WeakMap)记录已拷贝对象,防止无限递归。类型检测要放在循环检查之后,确保不重复处理同一引用。
- 每次进入递归前,先查
cache.has(obj),命中则直接返回缓存结果 -
cache.set(obj, result)应在创建新对象后、填充属性前完成,保证引用一致性 -
WeakMap适合键为对象,且不影响垃圾回收,比Map更安全
类型安全检测不是为了“炫技”,而是让深拷贝行为可预期:原始值不被意外包装,特殊对象不丢失特性,循环结构不崩溃。写的时候多一行 toString.call,能省掉后续大量调试时间。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










