string.prototype.normalize('nfkd')仅执行unicode标准化,将等价字符转为统一ascii兼容形式,是深度去重与语义匹配的前提而非全部;需叠加小写化、空白清理、标点过滤等步骤生成语义归一化键。

String.prototype.normalize('NFKD') 本身不直接实现“深度去重”或“语义匹配”,它只做 Unicode 标准化——把等价但编码不同的字符(如带音调的字母、全角标点、组合字符)转换成统一的、可比对的基础形式。真正的深度去重和语义匹配,需要在 normalize 之后叠加其他逻辑。
标准化是去重的前提:消除视觉相同、码点不同的干扰
很多看似重复的文本其实因 Unicode 表示不同而无法被简单 === 或 Set 识别。例如:
-
"café"(e 带重音符 U+00E9) vs"cafe\u0301"(e + 组合重音符号 U+0301) -
"123"(全角数字) vs"123"(ASCII 数字) -
"Hello"(全角英文字母) vs"Hello"
调用 .normalize('NFKD') 后,这些都会转为 ASCII 兼容形式(如 é → e + ´ → 进一步被过滤或忽略),为后续清洗打下基础。
配合正则清理,构建“语义等价键”用于去重
仅 normalize 不够。需进一步移除无关差异,生成稳定哈希键:
- 小写化:
.toLowerCase() - 去除多余空白:
.replace(/\s+/g, ' ').trim() - 可选:过滤标点/停用词(按需):
.replace(/[^\p{L}\p{N}\s]/gu, '') - 可选:统一空格与换行:
.replace(/[\r\n\t]+/g, ' ')
这样得到的字符串,才是适合放进 new Set() 或用作 Map 键的“语义归一化结果”。
语义匹配 ≠ 文本相等:normalize 是起点,不是终点
若目标是“语义匹配”(如判断“颜色”和“colour”是否算重复),NFKD 完全不处理拼写变体或同义词。此时应:
- 先 normalize 清洗编码噪声
- 再接入词干提取(如
porter-stemmer)、同义词映射(如 WordNet 或自定义表)、或嵌入向量相似度(如 sentence-transformers) - 对中文,还需分词+停用词处理,NFKD 对汉字本身几乎无影响(除非涉及异体字,且需搭配 NFKC 或额外映射表)
实用示例:轻量级去重函数
以下函数将原始字符串转为可比对的归一化键:
function normalizeForDedup(str) {
if (typeof str !== 'string') return '';
return str
.normalize('NFKD')
.toLowerCase()
.replace(/[\u2000-\u206F\u2E00-\u2E7F\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFE30-\uFE4F\uFE50-\uFE6F\uFE70-\uFEFF]/g, '') // 移除常见全角/标点/符号
.replace(/\s+/g, ' ')
.trim();
}
// 使用
const raw = ['café', 'cafe\u0301', ' Hello ', 'hello'];
const unique = [...new Set(raw.map(normalizeForDedup))]; // ['cafe', 'hello']










