必须用 Reflect.ownKeys,因为它唯一能同时返回所有自有字符串键和 Symbol 键;Object.keys 只返回可枚举字符串键,Object.getOwnPropertyNames 忽略 Symbol 键。

可以通过 Reflect.ownKeys 遍历对象所有自有属性键(含 Symbol),再结合预设脱敏规则对匹配的键名对应值进行替换或加密,从而实现真正全覆盖的自动化脱敏。
为什么必须用 Reflect.ownKeys 而不是 Object.keys 或 Object.getOwnPropertyNames
Object.keys 只返回可枚举字符串键;Object.getOwnPropertyNames 返回所有自有字符串键(含不可枚举),但依然忽略 Symbol 键;而 Reflect.ownKeys(obj) 是目前唯一能**同时返回所有自有字符串键和 Symbol 键**的标准 API,确保不漏掉任何敏感字段(比如 obj[Symbol.for('token')])。
定义脱敏规则:支持字符串与 Symbol 双模式匹配
规则需能识别两种键类型:既支持精确字符串匹配(如 'password'),也支持 Symbol 匹配(如 Symbol.for('authToken') 或任意 Symbol 实例)。建议将规则设计为数组,每项是函数或配置对象:
- 函数形式:
(key: PropertyKey) => boolean,直接判断是否需脱敏 - 配置形式:包含
keys: Array和mask: (val: any) => any,便于复用
例如:[<br> { keys: ['password', 'secret'], mask: () => '***' },<br> { keys: [Symbol.for('session'), Symbol('api_key')], mask: v => typeof v === 'string' ? v.slice(0, 4) + '...' : '[symbol masked]' }<br>]
递归遍历 + 键名驱动脱敏的核心逻辑
对目标对象执行深度遍历,每一层都调用 Reflect.ownKeys 获取全部键,再逐个检查是否命中脱敏规则。关键点:
- 对每个键
k,用Object.getOwnPropertyDescriptor(obj, k)获取描述符,保留enumerable、configurable、writable等元信息,避免破坏属性特性 - 若命中规则,用
Object.defineProperty替换值(而非简单赋值),确保不可写/不可配置属性仍保持原行为 - 遇到嵌套对象或数组,递归处理其元素(注意循环引用检测)
实际使用示例:封装成可复用的 maskObject 函数
以下是一个轻量实现(省略循环引用处理,生产环境建议加入 WeakMap 缓存):
function maskObject(obj, rules = []) {<br> if (obj == null || typeof obj !== 'object') return obj;<br> <br> const keys = Reflect.ownKeys(obj);<br> const result = Array.isArray(obj) ? [] : {};<br> <br> for (const k of keys) {<br> const desc = Object.getOwnPropertyDescriptor(obj, k);<br> let val = desc.value;<br> <br> // 判断是否需脱敏<br> const shouldMask = rules.some(rule => {<br> if (typeof rule === 'function') return rule(k);<br> if (Array.isArray(rule.keys)) return rule.keys.includes(k);<br> return false;<br> });<br> <br> if (shouldMask) {<br> const rule = rules.find(r => typeof r === 'function' ? r(k) : Array.isArray(r.keys) && r.keys.includes(k));<br> val = rule?.mask?.(val) ?? '[REDACTED]';<br> } else if (typeof val === 'object' && val !== null) {<br> val = maskObject(val, rules); // 递归<br> }<br> <br> Object.defineProperty(result, k, {<br> ...desc,<br> value: val<br> });<br> }<br> return result;<br>}这样就能安全处理 { password: '123', [Symbol('token')]: 'abc' } 等混合键名结构,且不破坏原有属性特性。










