JSON.stringify配合正则无法实现真正深拷贝脱敏,因其丢失类型信息、无法识别字段语义、易跨字段污染、不支持循环引用;应采用结构化递归遍历+规则配置方案。

JSON.stringify 配合正则表达式本身**不能可靠实现真正意义上的深拷贝过滤**,尤其在需要“脱敏”(如隐藏手机号、身份证号、密码等敏感字段)的场景下,这种组合存在严重局限性和安全隐患。它最多只能做**字符串层面的粗略替换**,无法替代结构化、语义化的数据处理逻辑。
为什么不能直接用 JSON.stringify + 正则做脱敏深拷贝?
原因很直接:
- 丢失类型信息:JSON.stringify 会把函数、undefined、Symbol、Date、RegExp、Map、Set、BigInt 等全部丢弃或转为 null/空对象,拷贝后不再是“深拷贝”,而是“JSON 序列化快照”。
-
正则无法识别字段语义:比如
"phone": "13812345678"和"remark": "用户电话是13812345678"在 JSON 字符串里都是普通文本,正则无法区分哪个是结构化敏感字段,哪个是普通描述——容易误脱敏或漏脱敏。 -
破坏嵌套结构与键名匹配:正则全局替换可能跨字段污染,例如匹配到
"id": "123456"中的数字,结果把用户 ID 也当成身份证号抹掉了。 - 无法处理循环引用:JSON.stringify 遇到循环引用直接报错,而真正的深拷贝需主动检测并处理。
更合理、安全的替代方案:结构化遍历 + 规则配置
真正可用的脱敏深拷贝,应基于递归遍历对象/数组,并按预定义规则对特定路径或字段名进行值替换。例如:
- 明确指定需脱敏的字段名:
["password", "idCard", "phone", "bankCard"] - 支持路径匹配:
"user.contact.mobile"或通配符"*.token" - 支持自定义脱敏函数:
(val) => val?.replace(/^(.{3}).*(.{4})$/, '$1****$2')
示例(轻量实现):
function sensitiveClone(obj, rules = {}) {
const { fields = [], paths = [], custom = {} } = rules;
const isSensitiveKey = key => fields.includes(key);
const isSensitivePath = (path = '') => paths.some(p =>
p.endsWith('*') ? path.startsWith(p.slice(0, -1)) : path === p
);
function walk(val, path = '') {
if (val === null || typeof val !== 'object') return val;
if (Array.isArray(val)) {
return val.map((item, i) => walk(item, `${path}[${i}]`));
}
// 普通对象
const result = {};
for (const [key, rawVal] of Object.entries(val)) {
const currPath = path ? `${path}.${key}` : key;
const replacer = custom[key] || custom[currPath];
if (isSensitiveKey(key) || isSensitivePath(currPath)) {
result[key] = replacer ? replacer(rawVal) : '***';
} else {
result[key] = walk(rawVal, currPath);
}
}
return result;
}
return walk(obj);
}
// 使用示例
const data = {
name: "张三",
phone: "13812345678",
user: {
idCard: "110101199001011234",
token: "abc-xzy-789"
}
};
const cloned = sensitiveClone(data, {
fields: ['phone', 'idCard'],
custom: { token: () => '[TOKEN_HIDDEN]' }
});
如果坚持用 JSON.stringify + 正则(仅限简单日志脱敏等低风险场景)
必须满足两个前提:数据纯 JSON 兼容(无函数/日期/循环)、且敏感字段名唯一不歧义。操作步骤如下:
- 先用
JSON.stringify(obj, null, 2)转为格式化字符串 - 用正则匹配
"phone"\s*:\s*"(.*?)"这类带引号的键值对(注意转义和贪婪控制) - 替换值部分,保留键名和结构,例如:
str.replace(/"phone"\s*:\s*"(.*?)"/g, '"phone": "***"') - 最后用
JSON.parse()恢复(但注意:已丢失所有非 JSON 类型)
⚠️ 再次强调:该方式脆弱、不可维护、易出错,**不推荐用于生产环境的敏感数据处理**。
总结:脱敏深拷贝的关键不在“怎么 stringify”,而在“怎么理解数据结构”
真正健壮的方案要能:
- 保持原始数据类型与结构完整性
- 按业务语义精准定位敏感节点(字段名、路径、甚至值特征)
- 支持可扩展的脱敏策略(掩码、哈希、删除、加密)
- 可审计、可测试、可配置,而非靠正则硬匹配
工具库如 lodash.cloneDeepWith 或专为脱敏设计的 @delvtech/sanitize、json-sanitizer 更值得考虑。


















