可利用 replace() 的捕获组引用(如 $1)实现轻量模板引擎,需用字面量正则匹配占位符,$1 在替换字符串中生效,但动态取值须用替换函数;注意 key 安全校验与 html 转义。

可以利用 String.prototype.replace() 的捕获组引用(如 $1、$2 等)实现一个轻量级、基于正则匹配的模板引擎,无需引入外部库。它的核心在于:用正则提取占位符,再通过替换函数或字符串替换语法将对应数据注入。
理解 $1 等捕获组引用的工作机制
$1 表示正则中第一个 () 捕获组匹配到的内容,$2 是第二个,以此类推;$& 表示整个匹配项,$` 和 $' 分别是匹配前/后的字符串。这些符号只在 replace() 的**替换字符串参数**中生效(非函数参数),属于原生字符串插值能力。
- 必须使用字面量正则(如
/\{(\w+)\}/g),且含捕获括号 - 替换字符串中写
"$1",JS 会自动将其解析为第一个捕获内容 - 不支持动态计算,仅做纯文本映射 —— 若需逻辑处理,必须改用替换函数
基础模板渲染:用 $1 替换单个变量
例如模板 "Hello {name}, you have {count} messages",希望把 {name} 和 {count} 替换成实际值:
const template = "Hello {name}, you have {count} messages";
const data = { name: "Alice", count: 5 };
const result = template.replace(/\{(\w+)\}/g, (match, key) => data[key]);
// → "Hello Alice, you have 5 messages"
注意:这里用了**替换函数**而非 $1 字符串语法,因为 $1 无法访问 data 对象。若坚持用 $1,只能做固定映射(如全部 {name} 强制替换成 "Guest"),实用性低。真正发挥 $1 价值的场景,是配合预定义映射表或编译阶段处理。
进阶技巧:用 $1 实现简单指令式模板(如 if/upper)
可设计带前缀的语法,如 {%upper:name%} 或 {?active:yes:no},用正则捕获指令名和参数,再在替换函数中分发处理:
const template = "Status: {%upper:status%}, Active: {?isActive:✔:✘}";
template.replace(
/{%(\w+):(\w+)%}|{\?(\w+):(\w+):(\w+)}/g,
(match, cmd, key1, cond, yes, no) => {
if (cmd) return data[key1]?.toUpperCase?.() ?? "";
if (cond) return data[cond] ? yes : no;
return match;
}
);
此处虽未直接用 $1 做最终输出,但正则中的 (\w+) 捕获结果被函数参数接收(cmd, key1 等),本质仍是捕获组驱动逻辑 —— $1 是底层机制,函数是其自然延伸。
安全与边界:避免意外替换和原型污染
用户输入的 key 可能含点号(user.profile.name)或恶意字段(constructor、__proto__)。应限制 key 格式并安全取值:
- 正则改用
/\{([a-zA-Z_$][a-zA-Z0-9_$]*)\}/g,只允许合法标识符 - 取值不用
data[key],改用Object.prototype.hasOwnProperty.call(data, key) && data[key] - 对输出做 HTML 转义(如
text.replace(/&/g,"&").replace(/,"),防止 XSS
不复杂但容易忽略:$1 本身不是模板引擎,而是正则替换的语法糖;真正的模板能力来自捕获 + 函数分发 + 数据安全访问 —— 把它看作“胶水”,而不是“引擎”。










