实现模板字符串解析功能
手写实现类似 ES6 模板字符串的解析功能,支持变量插值和表达式计算
问题
实现一个函数,能够解析模板字符串,将其中的变量占位符替换为实际的值。类似于 ES6 的模板字符串功能,但需要手动实现解析逻辑。
例如:将 "Hello, ${name}! You are ${age} years old." 这样的字符串,根据提供的数据对象解析成 "Hello, Tom! You are 18 years old."
解答
/**
* 模板字符串解析函数
* @param {string} template - 模板字符串
* @param {object} data - 数据对象
* @returns {string} 解析后的字符串
*/
function parseTemplate(template, data) {
// 使用正则表达式匹配 ${...} 格式的占位符
return template.replace(/\$\{([^}]+)\}/g, (match, key) => {
// key 是捕获组中的内容,即 ${} 中间的部分
const trimmedKey = key.trim();
// 支持表达式计算
try {
// 创建一个函数来安全地执行表达式
const func = new Function(...Object.keys(data), `return ${trimmedKey}`);
const result = func(...Object.values(data));
// 如果结果是 undefined 或 null,返回空字符串
return result !== undefined && result !== null ? result : '';
} catch (e) {
// 如果表达式执行失败,尝试直接取值
return data[trimmedKey] !== undefined ? data[trimmedKey] : match;
}
});
}
/**
* 简化版本:仅支持简单的属性访问(包括嵌套属性)
* @param {string} template - 模板字符串
* @param {object} data - 数据对象
* @returns {string} 解析后的字符串
*/
function parseTemplateSimple(template, data) {
return template.replace(/\$\{([^}]+)\}/g, (match, key) => {
const trimmedKey = key.trim();
// 支持嵌套属性访问,如 user.name
const value = trimmedKey.split('.').reduce((obj, prop) => {
return obj && obj[prop] !== undefined ? obj[prop] : undefined;
}, data);
return value !== undefined && value !== null ? value : '';
});
}
/**
* 增强版本:支持默认值
* @param {string} template - 模板字符串
* @param {object} data - 数据对象
* @param {string} defaultValue - 默认值
* @returns {string} 解析后的字符串
*/
function parseTemplateWithDefault(template, data, defaultValue = '') {
return template.replace(/\$\{([^}]+)\}/g, (match, key) => {
const trimmedKey = key.trim();
// 支持默认值语法:${name || 'Anonymous'}
if (trimmedKey.includes('||')) {
const [varName, defVal] = trimmedKey.split('||').map(s => s.trim());
const value = varName.split('.').reduce((obj, prop) => {
return obj && obj[prop] !== undefined ? obj[prop] : undefined;
}, data);
return value !== undefined && value !== null && value !== ''
? value
: defVal.replace(/['"]/g, '');
}
const value = trimmedKey.split('.').reduce((obj, prop) => {
return obj && obj[prop] !== undefined ? obj[prop] : undefined;
}, data);
return value !== undefined && value !== null ? value : defaultValue;
});
}
使用示例
// 示例1:基本使用
const template1 = "Hello, ${name}! You are ${age} years old.";
const data1 = { name: "Tom", age: 18 };
console.log(parseTemplate(template1, data1));
// 输出: "Hello, Tom! You are 18 years old."
// 示例2:支持表达式计算
const template2 = "The sum is ${a + b}, and the product is ${a * b}.";
const data2 = { a: 5, b: 3 };
console.log(parseTemplate(template2, data2));
// 输出: "The sum is 8, and the product is 15."
// 示例3:支持嵌套属性访问
const template3 = "User: ${user.name}, Email: ${user.email}";
const data3 = {
user: {
name: "Alice",
email: "alice@example.com"
}
};
console.log(parseTemplateSimple(template3, data3));
// 输出: "User: Alice, Email: alice@example.com"
// 示例4:支持默认值
const template4 = "Welcome, ${username || 'Guest'}!";
const data4 = { username: "" };
console.log(parseTemplateWithDefault(template4, data4));
// 输出: "Welcome, Guest!"
// 示例5:处理不存在的变量
const template5 = "Hello, ${name}! Your score is ${score}.";
const data5 = { name: "Bob" };
console.log(parseTemplate(template5, data5));
// 输出: "Hello, Bob! Your score is ."
// 示例6:复杂表达式
const template6 = "Price: $${price}, Tax: $${price * 0.1}, Total: $${price * 1.1}";
const data6 = { price: 100 };
console.log(parseTemplate(template6, data6));
// 输出: "Price: $100, Tax: $10, Total: $110"
关键点
-
正则表达式匹配:使用
/\$\{([^}]+)\}/g匹配所有${...}格式的占位符,其中([^}]+)捕获大括号内的内容 -
replace 回调函数:
replace方法的第二个参数可以是函数,接收匹配到的字符串和捕获组作为参数 -
动态表达式执行:使用
new Function()可以动态执行字符串形式的 JavaScript 表达式,实现计算功能 -
嵌套属性访问:使用
reduce方法遍历属性路径,支持user.name这样的嵌套访问 -
异常处理:使用
try-catch捕获表达式执行错误,提供降级方案 -
边界情况处理:对
undefined、null、空字符串等特殊值进行判断和处理 -
安全性考虑:在生产环境中使用
new Function()需要注意安全风险,应该对输入进行验证和过滤,或使用更安全的解析方式
目录