闭包为脱敏逻辑提供灵活、可复用、带状态的封装方式,真正起作用的是字符串处理逻辑;它支持按需定制起始位、保留位数、掩码字符等参数,并在多次调用中保持配置不变。

闭包本身不直接“实现脱敏”,而是为脱敏逻辑提供一种灵活、可复用、带状态的封装方式。真正起作用的是字符串处理逻辑(如正则替换),而闭包让这个逻辑能按需定制起始位、保留位数、掩码字符等参数,并在多次调用中保持配置不变。
为什么用闭包写脱敏过滤器?
直接写死规则(比如固定隐藏手机号中间4位)不够通用。业务中常需要:
- 同一页面里,有的手机号显示“138****5678”,有的要显示“138****56**”(后两位也掩)
- 姓名脱敏:两字名“李*”,三字名“王**”,四字名“张*某*”——规则不同
- 不想每处都重复写
str.replace(/^(.{3}).*(.{4})$/, '$1****$2')
闭包能把“配置参数 + 处理函数”打包成一个可复用的工厂函数,调用一次就生成一个专属过滤器。
一个典型的闭包脱敏工厂函数
这是知识库中提到的万能脱敏方法的简化实现(TypeScript风格,但逻辑通用):
// getStr 返回一个函数,该函数记住了 beginLen 和 endLen
function getStr(beginLen, endLen, mask = '*') {
return function(str) {
if (!str || typeof str !== 'string') return str;
const len = str.length;
if (len
const maskedPart = mask.repeat(len - beginLen - endLen);
return str.slice(0, beginLen) + maskedPart + str.slice(-endLen);
};
}
使用示例:
-
手机号(前三后四):
const phoneFilter = getStr(3, 4); phoneFilter('13812345678') → '138****5678' -
身份证(前六后四):
const idFilter = getStr(6, 4); idFilter('330106199001011234') → '330106********1234' -
姓名(两字留首,三字留首尾):
const nameFilter = getStr(1, str => str.length > 2 ? 1 : 0);(稍作扩展即可支持动态 endLen)
如何在渲染前自动应用?
关键不是“自动”,而是“统一接入点”。闭包过滤器需配合框架机制才能做到“渲染前处理”:
-
Vue 全局过滤器:把
getStr(3, 4)的结果注册为phoneMask过滤器,在模板中直接写{{ item.phone | phoneMask }} -
React 自定义 Hook:封装
useMaskedValue(value, strategy),内部调用闭包生成的过滤函数,返回脱敏后值 -
JSON 序列化拦截(后端常用):如 Spring Boot 中用
@JsonSerialize注解 + 自定义序列化器,该序列化器内部调用闭包式脱敏工具(如 Hutool 的DesensitizedUtil就是典型闭包封装)
注意:前端过滤器只用于展示层,不能替代后端脱敏或权限控制。敏感数据仍需服务端严格校验与最小化返回。
闭包带来的实际好处
它让脱敏从“写死逻辑”变成“配置即代码”:
- 规则变更只需改调用参数,不用动正则表达式本身
- 不同字段可共用同一工厂,避免重复造轮子
- 便于单元测试:传入不同参数,验证输出是否符合预期
- 内存开销极小,现代 JS 引擎对简单闭包优化很好
不复杂但容易忽略:闭包只是组织方式,真正安全依赖的是规则设计是否合理、掩码是否足够强、以及前后端职责是否清晰。

















