打算重写一个模板类,先学习一下John Resig大师是怎么写的:
// http://ejohn.org/blog/javascript-micro-templating/
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
为了便于一步一步分析,我仿照着重写了一遍,并且加上了注释:
(function(){
// 模板缓存, 键是 id, 值是已生成的模板函数
var cache = {};
// 定义 window.tmpl
this.tmpl = function tmpl(str, data){
var fn;
// 如果 str 中不存在"非单词字符"
if ( ! /\W/.test(str)){
// 即 str 是 id
if ( ! cache[str]){
// 如果在缓存里找不到该模板,就调用自身生成模板函数,并放入缓存
cache[str] = tmpl(document.getElementById(str).innerHTML);
}
fn = cache[str];
} else {
// str 是模板内容,包含字符串部分和脚本部分,需要转换成纯js
str = str.replace(/[\r\t\n]/g, ' '); // 替换 换行/回车/制表符 为 空格
str = str.split('<%').join('\t'); // 替换全部 <% 为 \t
str = str.replace(/((^|%>)[^\t]*)'/g, '$1\r'); // 临时模板中非脚本部分内的 ' 为 \r
str = str.replace(/\t=(.*?)%>/g, "',$1,'"); // 分离 变量字符串
str = str.split('\t').join("');"); // 分隔模板中的字符串部分和脚本部分
str = str.split('%>').join("p.push('"); // 同上
str = str.split('\r').join("\\'"); // 还原模板中字符串部分的单引号 '
var func = "var p=[];"+
"var print=function(){"+ // print 函数没有用到
"p.push.apply(p,arguments);"+
"};" +
"with(obj){"+
"p.push('"+str+"')"+
"}"+
"return p.join('');";
// 生成解析函数
fn = new Function('obj', func);
}
return data ? fn( data ) : fn;
};
})();
这个模板类的使用方法相当灵活,模板全面支持 javascript 语法,if、for、while等等,因为它将模板字符串解析成了纯 javascript 函数。