最可靠的方式是手动递归遍历 + 条件过滤,支持嵌套对象、数组、date、regexp等,自动跳过原型链属性和指定忽略键,可扩展循环引用处理;structuredclone仅适用于现代环境且需预处理剥离属性。

JavaScript 中实现对象深拷贝并忽略特定属性,最可靠的方式是手动递归遍历 + 条件过滤,而不是依赖 JSON.parse(JSON.stringify())(它会丢函数、undefined、Symbol、循环引用等)或浅层库方法(如 Lodash 的 _.omit() 不做深拷贝)。
使用递归函数实现带过滤的深拷贝
核心思路:对每个属性判断是否在忽略列表中;若不是,再根据值类型决定是直接复制(基本类型)、递归处理(对象/数组)、还是跳过(null/undefined 等)。
- 支持普通对象、数组、嵌套结构
- 可传入忽略属性名数组(如
['password', 'id', '__meta']) - 自动跳过原型链属性(只处理自有可枚举属性)
- 对 Date、RegExp 等特殊对象可扩展处理逻辑
示例代码:
function deepCloneWithout(obj, omitKeys = []) {
if (obj === null || typeof obj !== 'object') return obj;
// 处理 Date 和 RegExp 等内置对象
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 创建新容器(数组 or 对象)
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
if (omitKeys.includes(key)) continue;
const value = obj[key];
result[key] = deepCloneWithout(value, omitKeys);
}
return result;
}
// 使用示例
const user = {
name: 'Alice',
password: '123456',
profile: { age: 30, token: 'abc', settings: { theme: 'dark' } },
tags: ['admin', 'user'],
id: 1001
};
const cloned = deepCloneWithout(user, ['password', 'id']);
// → { name: 'Alice', profile: { age: 30, settings: { theme: 'dark' } }, tags: ['admin', 'user'] }
用 structuredClone 配合预处理(现代环境推荐)
如果运行环境支持 structuredClone(Chrome 98+、Firefox 94+、Node.js 17.0+),可先 shallow omit 再深克隆,避免手写递归。注意:structuredClone 本身不支持忽略属性,需提前剥离。
- 先用
Object.fromEntries()或解构 + rest 拆出要保留的键 - 再对精简后的对象调用
structuredClone - 适合扁平或中等嵌套结构;对深层嵌套仍建议递归方案
示例:
function cloneWithoutModern(obj, omitKeys) {
if (obj === null || typeof obj !== 'object') return obj;
const filtered = Object.fromEntries(
Object.entries(obj).filter(([key]) => !omitKeys.includes(key))
);
return structuredClone(filtered);
}
处理循环引用和不可枚举属性(进阶需求)
标准递归方案默认不处理循环引用,容易爆栈。如需健壮性支持,可引入 WeakMap 缓存已拷贝对象:
- 每次进入对象前查缓存,命中则直接返回引用(保持结构一致性)
- WeakMap 键为原对象,值为对应克隆体
- 不可枚举属性需用
Object.getOwnPropertyNames()+Object.getOwnPropertyDescriptors()获取,但通常业务中忽略即可
简单增强版示意:
function deepCloneWithoutSafe(obj, omitKeys = [], seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
const result = Array.isArray(obj) ? [] : {};
seen.set(obj, result);
for (const key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key) || omitKeys.includes(key)) continue;
result[key] = deepCloneWithoutSafe(obj[key], omitKeys, seen);
}
return result;
}
为什么不推荐 JSON 方案?
JSON.parse(JSON.stringify(obj)) 看似简洁,但有严重限制:
- 丢失函数、undefined、Symbol、BigInt
- Date 变成字符串,RegExp 变成空对象
- 无法处理循环引用(直接报错)
- 不能过滤属性——必须先删再序列化,且删操作本身可能误伤嵌套同名字段
除非你确定数据纯 JSON 兼容且无敏感结构,否则不建议用于生产环境的“忽略式深拷贝”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











