javascript柯里化模板函数可将模板定义与数据传入分离,实现延迟渲染:先接收普通字符串模板(如"hello ${name}"),再通过正则匹配${key}并安全取值,支持点号路径访问和默认值处理,且不依赖eval或with。

JavaScript 中可以通过柯里化函数将模板字符串的插值过程拆分为“定义模板”和“传入数据”两个阶段,实现延迟渲染。核心思路是:先接收模板字符串,返回一个等待参数的函数;该函数内部用 String.raw 或正则匹配占位符(如 ${key}),再用传入的数据对象做安全替换。
基础柯里化模板函数
不依赖外部库,手动实现一个支持单次插值的柯里化模板函数:
- 用
Function.prototype.bind或箭头函数实现参数分步接收 - 模板中使用标准
${key}语法,但注意:JS 模板字面量本身无法延迟求值,所以需把模板写成普通字符串(如"Hello ${name}, you have ${count} messages") - 替换时避免直接
eval或with,改用Object.hasOwnProperty+ 字符串正则安全取值
示例代码:
const curryTemplate = (template) => (data) => {
return template.replace(/\$\{([^}]+)\}/g, (match, key) => {
const trimmedKey = key.trim();
return Object.prototype.hasOwnProperty.call(data, trimmedKey)
? data[trimmedKey]
: match; // 未提供时保留原占位符,便于调试
});
};
<p>// 使用
const welcomeMsg = curryTemplate('Hello ${name}, you have ${count} messages');
console.log(welcomeMsg({ name: 'Alice', count: 5 }));
// → "Hello Alice, you have 5 messages"
</p>支持嵌套属性与默认值的增强版
实际项目中常需访问 ${user.profile.name} 或提供默认值 ${count || 0}。纯正则难以安全执行表达式,推荐简化规则:
- 只支持点号路径访问(如
${user.name}),用key.split('.').reduce(...)安全取值 - 默认值可约定语法,例如
${count ?? 0},单独解析??左右部分 - 更稳妥的做法是:模板函数只处理简单键名,复杂逻辑交由预处理数据完成
点号路径示例:
const safeGet = (obj, path) => {
return path.split('.').reduce((o, k) => (o && typeof o === 'object' ? o[k] : undefined), obj);
};
<p>const curryTemplateDeep = (template) => (data) => {
return template.replace(/\${([^}]+)}/g, (match, key) => {
const trimmed = key.trim();
if (trimmed.includes('.')) {
return safeGet(data, trimmed) ?? match;
}
return Object.prototype.hasOwnProperty.call(data, trimmed)
? data[trimmed]
: match;
});
};
</p>结合 tagged template literals 的灵活方案
若想保留原生模板字面量写法(如 `Hello ${name}`),可用带标签的模板字符串 + 柯里化组合:
- 定义一个标签函数
curry`...`,它不立即执行,而是返回一个接收数据的函数 - 利用
strings数组和placeholders占位符数组,在最终调用时拼接 - 注意:此时插值表达式(如
${name})在定义时已被 JS 执行——所以必须用字符串字面量绕过,即写成curry`Hello \${name}`,再手动解析\${...}
实用折中写法(推荐):
const curry = (strings, ...keys) => (data) => {
return strings.reduce((result, str, i) => {
const value = i // 使用:把变量名当参数传入,不执行原始插值
const greeting = curry<code>Hello ${'name'}, you have ${'count'} messages</code>;
console.log(greeting({ name: 'Bob', count: 3 }));
// → "Hello Bob, you have 3 messages"
注意事项与边界处理
延迟渲染模板容易忽略的安全与健壮性问题:
- 永远校验数据是否存在,避免
undefined.name报错;建议统一用空字符串或??fallback - HTML 渲染场景下,必须对插值内容做转义(如
→ <code><),防止 XSS;可组合DOMPurify.sanitize()或简易转义函数 - 不要在模板函数里支持任意 JS 表达式(如
${a + b}),这等价于eval,应由调用方预先计算好字段 - 性能敏感场景可提前编译正则、缓存解析结果,或用
new Function()动态生成渲染函数(需确保输入可信)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











