模版替换技术
进入主题之前先介绍一种创建函数的新方法:new Function()
我们来创建一个函数,名字为foo
function foo(a, b){ return a + b; }
这种方式是我们普遍使用的,但同样的,我们还可以使用JS语言内置的Function对象来创建这个函数
var foo = new Function('a', 'b', 'return a + b;');
其实这两种写法在JS引擎看来是同一种,因为第一种会被转换为第二种,只不过第一种可读性更好而已。
例子说完了,来看下API:
/** * 函数名称为 funName * @param {String} p1 第一个参数名称 * @param {String} p2 第二个参数名称 * ... * @param {String} body 函数体 */ var funName = new Function(p1, p2, ..., pn, body);
需要注意的是,p1 到 pn 是参数名称的列表,但 p1 不仅能代表一个参数,也可以是一个逗号隔开的参数列表,所以下面的定义是等价的:
new Function('a', 'b', 'c', 'return a + b + c;'); new Function('a, b', 'c', 'return a + b + c;'); new Function('a, b, c', 'return a + b + c;');
现在进入正题,我在John Resig的博客里发现了这个函数:
// Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed (function(){ var cache = {}; this.tmpl = function tmpl(str, data){ // 查找非单词字符,如<>,没找到表示是ID,找到了表示是模板文本 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; }; })();
核心是中间那个函数的逻辑,这里我写一个简单的模板:
var str = '<div class="<%=clsName%>">' + '<h1><%=title%></h1>' + '<p><%=content%></p>' + '</div>'; var result = tmpl(str); console.log(result);
结果为:
function anonymous(obj) { var p = [], print = function(){ p.push.apply(p, arguments); }; with(obj){ p.push('<div class="', clsName, '"><h1>', title, '</h1><p>', content, '</p></div>'); } return p.join(''); }
到此, 相信大家都知道这个函数做了什么事了。
作者最后分享了一个查找替换的小技巧,通常我们会用str.replace(/match/g, 'replace'),但是性能上还有一种更好的:
str.split('match').join('replace');