浅析Prototype的模板类 Template


Posted in Javascript onDecember 07, 2011

用过Prototype的人都知道,里面有个类叫做Template,用法示例如下:

var str = '#{what} may have gone, but there is a time of #{how}'; 
var object = { 
what : 'Swallows', 
how : 'return' 
} 
var template_1 = new Template(str); 
var result = template_1.evaluate(object); console.log('result:',result); 
//输出:'Swallows may have gone, but there is a time of return'

这么挺方便的,所以下面就简单的分析一下实现原理,也算是源码解读的一个笔记。

我们先看一下一般需求里面会用到的一些情况,还是用上面的例子,先决定我们的形式,替换的部分是形如#{what}的内容,其中what是一个object对象的一个关键字。
现在有个问题就是,如果object是一个嵌套的对象,我们该怎么替换?
即:

<script type="text/javascript"> 
var object = { 
what : { 
name : 'Swallows' 
}, 
how : 'return' 
} 
</script>

最开始的#{what}肯定不能满足要求,所以我们硬性规定,如果要实现嵌套对象的替换,写作#{what.name}或者#{what[name]}。

所以最开始的例子可以写作:

<script type="text/javascript"> 
var str = '#{what.name} may have gone, but there is a time of #{how}'; 
//或者str = '#{what[name]} may have gone, but there is a time of #{how}'; 
var object = { 
what : { 
name : 'Swallows' 
}, 
how : 'return' 
} 
var template_1 = new Template(str); 
var result = template_1.evaluate(object); console.log('result:',result); 
//输出:'Swallows may have gone, but there is a time of return' 
</script>

源码里面有个正则var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;就是用来实现这个目的的。依次类推,任何深层次的嵌套都可以实现。

要做替换,我们最核心的就是要有一个替换字符的方法,源码里面用的是gsub,下面给出一个gsub的最简版本:

<script type="text/javascript"> 
function gsub(str,pattern, replacement){ 
var result = '', 
source = str, 
match; 
//下面的每一次匹配都是分成三段来操作的 
//相当于 $` $& $' 
while(source.length > 0){ 
match = source.match(pattern); 
if(match){ 
result += source.slice(0, match.index); 
result += replacement(match); 
source = source.slice(match.index + match[0].length); 
}else{ 
result += source; 
source = ''; 
} 
} 
return result; 
} 
</script>

这个调用方法和原理跟我前面涉及的replace类似,基本可以互换。http://www.cnblogs.com/xesam/archive/2011/12/05/2276783.html
<script type="text/javascript"> 
console.log(gsub('there is a time of #{how}',/(^|.|\r|\n)(#\{(.*?)\})/,function{ 
return 'demo'; 
})) 
//输出there is a time ofdemo 
</script>

下面回到Template来,基本要求:有类有方法

<script type="text/javascript"> 
var Template = function(template, pattern){ 
this.template = template.toString(); 
this.pattern = pattern || Template.Pattern; 
}; 
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
Template.prototype.evaluate = function(object){} 
</script>

template就是最初例子中的str,pattern是匹配规则,object就是最初例子中的object;现在重点剩下evaluate方法的实现:
直接调用gsub方法,主要是gsub中replacement的方法了。
可能出现的情况,
第一、object是一个空对象,那么我们直接删除需要替换的部分
比如

var str = '#{what} may have gone, but there is a time of #{how}';
var object = {}
那么就直接返回' may have gone, but there is a time of '

第二、转义部分直接保留

var str = '\\#{what} may have gone, but there is a time of \\#{how}'; 
var object = { 
what : 'Swallows', 
how : 'return' 
}

那么就直接返回'\\#{what} may have gone, but there is a time of \\#{how}';

这些情况在代码中都要处理,具体代码如下

Template.prototype.evaluate = function(object){ 
//gsub(str,pattern, replacement) 
return gsub(this.template,this.pattern,function(match){ 
var before = match[1];//这里的match[1]就是Template.Pattern中(^|.|\r|\n)匹配的部分 
var content = match[2];//这里的match[1]就是Template.Pattern中(#\{(.*?)\})匹配的部分 
var expr = match[3];//这里的match[1]就是Template.Pattern中(.*?)匹配的部分 
//示例: 
//对于 s#{what} 来说before='s',content='#{what}',expr='what' //第一、object是一个空对象,那么我们直接删除需要替换的部分 
if(object == null){ 
return (match[1] + ''); 
} 
//第二、转义部分直接保留 
if (before == '\\'){ 
return content; 
} 
//除了上面的两种情况,下面是正常的替换流程。 
var ctx = object; 
//下面这个正则我在前面说过,是为了匹配嵌套对象的。看最后面的(\.|\[|$)是为了确定有没有嵌套的子对象匹配。 
//#{what.name}中的'.' 
//或者#{what[name]}中的'[' 
//也就是下面的match[3] 
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 
match = pattern.exec(expr); 
while (match != null) { 
if(/^\[/.test(match[1])){ 
var comp = match[2].replace(/\\\\]/g, ']'); 
}else{ 
var comp = match[1]; 
} 
ctx = ctx[comp];//如果ctx[comp]是一个字符串,那么下面一步就可以收工了,如果ctx[comp]还是一个对象,那么抱歉,加班继续干吧。 
if (null == ctx || '' == match[3]){//需要替换的不是object的嵌套子对象,那么就直接中断循环。替换成功,下班。 
break; 
} 
//下面的仅仅是为了剥离出来关键字,其他操作用在循环里面再来一次。 
if('[' == match[3]){ 
expr = expr.substring(match[1].length) 
}else{ 
expr = expr.substring(match[0].length) 
} 
match = pattern.exec(expr); 
} 
return before + ctx; 
}); 
};

当然,源码中并没有这么简单,还参杂了一写检测和判断,比如替换str的非法字符,处理replacement为字符串的情况等等。具体可以去查看源码。
可能有些复杂的就是处理replacement为字符串的情况,gsub和Template有一个嵌套调用的情况,不过意思都一样,最后丢回一个函数就可以了。
Javascript 相关文章推荐
js动态添加事件并可传参数示例代码
Oct 21 Javascript
js计算文本框输入的字符数
Oct 23 Javascript
Bootstrap精简教程
Nov 27 Javascript
AngularJs ng-route路由详解及实例代码
Sep 14 Javascript
AngularJS中关于ng-class指令的几种实现方式详解
Sep 17 Javascript
微信小程序实现图片自适应(支持多图)
Jan 25 Javascript
Ajax和Comet技术总结
Feb 19 Javascript
详解Vue爬坑之vuex初识
Jun 14 Javascript
vue实现配置全局访问路径头(axios)
Nov 01 Javascript
javascript事件循环event loop的简单模型解释与应用分析
Mar 14 Javascript
微信小程序返回上一级页面的实现代码
Jun 19 Javascript
Nuxt的路由动画效果案例
Nov 06 Javascript
js 幻灯片的实现
Dec 06 #Javascript
字符串的replace方法应用浅析
Dec 06 #Javascript
js滚动条回到顶部的代码
Dec 06 #Javascript
javascript检测浏览器flash版本的实现代码
Dec 06 #Javascript
javascript 随机展示头像实现代码
Dec 06 #Javascript
jQuery中需要注意的细节问题小结
Dec 06 #Javascript
jQuery load方法用法集锦
Dec 06 #Javascript
You might like
PHP产生随机字符串函数
2006/12/06 PHP
不用mod_rewrite直接用php实现伪静态化页面代码
2008/10/04 PHP
简单实用的PHP防注入类实例
2014/12/05 PHP
PHP实现加强版加密解密类实例
2015/07/29 PHP
PHP filter_var() 函数, 验证判断EMAIL,URL等
2021/03/09 PHP
爱恋千雪-US-AscII加密解密工具(网页加密)下载
2007/06/06 Javascript
Jquery 获取表单text,areatext,radio,checkbox,select值的代码
2009/11/12 Javascript
js类型检查实现代码
2010/10/29 Javascript
jQuery图片滚动图片的效果(另类实现)
2013/06/02 Javascript
jquery等待效果示例
2014/05/01 Javascript
jQuery实现Meizu魅族官方网站的导航菜单效果
2015/09/14 Javascript
JavaScript实现的伸展收缩型菜单代码
2015/10/14 Javascript
利用fecha进行JS日期处理
2016/11/21 Javascript
javascript十六进制数字和ASCII字符之间的转换方法
2016/12/27 Javascript
jQuery Validate 数组 全部验证问题
2017/01/12 Javascript
浅谈vue中数据双向绑定的实现原理
2017/09/14 Javascript
jquery在启动页面时,自动加载数据的实例
2018/01/22 jQuery
微信小程序设置全局请求URL及封装wx.request请求操作示例
2019/04/02 Javascript
[06:25]第二届DOTA2亚洲邀请赛主赛事第二天比赛集锦.mp4
2017/04/03 DOTA
[48:11]完美世界DOTA2联赛 Magma vs GXR 第二场 11.07
2020/11/10 DOTA
Python动态加载模块的3种方法
2014/11/22 Python
python ddt数据驱动最简实例代码
2019/02/22 Python
python中的逆序遍历实例
2019/12/25 Python
pycharm 代码自动补全的实现方法(图文)
2020/09/18 Python
如何利用pycharm进行代码更新比较
2020/11/04 Python
MATCHESFASHION.COM法国官网:英国奢侈品零售商
2018/01/04 全球购物
Linux管理员面试经常问道的相关命令
2013/04/29 面试题
城建学院毕业生自荐信
2014/01/31 职场文书
《画风》教学反思
2014/04/16 职场文书
质量安全标语
2014/06/07 职场文书
2014年教师批评与自我批评思想汇报
2014/09/20 职场文书
工程技术员岗位职责
2015/04/11 职场文书
2015年七七事变78周年纪念活动方案
2015/05/06 职场文书
英文投诉信格式
2015/07/03 职场文书
销售人员管理制度
2015/08/06 职场文书
Redis缓存-序列化对象存储乱码问题的解决
2021/06/21 Redis