@chris 的回答非常好,我只想提供一个使用正则表达式的替代解决方案,它可以“反过来”工作,即,不是通过查找属性中所有项目的“占位符版本”的出现map,但通过重复查找占位符本身的出现,并用属性映射中的相应值替换它。这有两个好处:
- 如果属性图变得非常大,这个解决方案应该有
更好的性能(但仍有待基准测试)。
- 可以通过调整正则表达式和替换函数轻松修改占位符和替换的工作方式(这里可能不是问题)。
当然,缺点是代码稍微复杂一些(部分原因是 JavaScript 缺乏使用自定义函数替换正则表达式匹配的好方法,所以这就是 substituteRegExp 的用途):
function substituteRegExp(string, regexp, f) {
// substitute all matches of regexp in string with the value
// returned by f given a match and the corresponding group values
var found;
var lastIndex = 0;
var result = "";
while (found = regexp.exec(string)) {
var subst = f.apply(this, found);
result += string.slice(lastIndex, found.index) + subst;
lastIndex = found.index + found[0].length;
}
result += string.slice(lastIndex);
return result;
}
function templateReplace(string, values) {
// repeatedly substitute [key] placeholders in string by values[key]
var placeholder = /\[([a-zA-Z0-9]+)\]/g;
while (true) {
var newString = substituteRegExp(string, placeholder, function(match, key) {
return values[key];
});
if (newString == string)
break;
string = newString;
}
return string;
}
alert(templateReplace("hello [[b]] [my] [name]", {
"name":"world",
"my":"beautiful",
"a":"[b]",
"b":"c",
"c":"my"
})); // -> "hello my beautiful world"
更新:我做了一些小分析来比较这两种解决方案(http://jsfiddle.net/n8Fyv/1/ 的 jsFiddle,我也使用了 Firebug)。虽然@chris 的解决方案对于小字符串更快(无需解析正则表达式等),但该解决方案对于大字符串(大约数千个字符)执行得更好。我没有比较不同大小的属性图,但预计会有更大的差异。
理论上,该解决方案的运行时间为 O(k n),其中 k 是占位符的嵌套深度,n 是字符串的长度(假设字典/哈希查找需要恒定时间),而@chris 的解决方案是 O(k n m) 其中 m 是属性映射中的项目数。当然,所有这些都只与大输入相关。