浅析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(DHTML)中的一些技巧
Jan 09 Javascript
js获取时间(本周、本季度、本月..)
Nov 22 Javascript
用JavaScript判断CSS浏览器类型前缀的两种方法
Oct 08 Javascript
教你一步步用jQyery实现轮播器
Dec 18 Javascript
angular实现spa单页面应用实例
Jul 10 Javascript
jQuery使用动画队列自定义动画操作示例
Jun 16 jQuery
jquery 动态遍历select 赋值的实例
Sep 12 jQuery
如何去除富文本中的html标签及vue、react、微信小程序中的过滤器
Nov 21 Javascript
vue-music 使用better-scroll遇到轮播图不能自动轮播问题
Dec 03 Javascript
使用Three.js实现太阳系八大行星的自转公转示例代码
Apr 09 Javascript
微信小程序实现点击导航条切换页面
Nov 19 Javascript
vue-router路由懒加载及实现的3种方式
Feb 28 Vue.js
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单件模式结合命令链模式使用说明
2008/09/07 PHP
解析PHP跨站刷票的实现代码
2013/06/18 PHP
提交表单后 PHP获取提交内容的实现方法
2016/05/25 PHP
php中preg_replace正则替换用法分析【一次替换多个值】
2017/01/17 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
javascript 处理HTML元素必须避免使用的一种方法
2009/07/30 Javascript
JavaScript读取中文cookie时的乱码问题的解决方法
2009/10/14 Javascript
javascript Firefox与IE 替换节点的方法
2010/02/24 Javascript
提高NodeJS中SSL服务的性能
2014/07/15 NodeJs
JS在一定时间内跳转页面及各种刷新页面的实现方法
2016/05/26 Javascript
自学实现angularjs依赖注入
2016/12/20 Javascript
Angularjs为ng-click事件传递参数
2017/06/15 Javascript
vue-cli如何引入bootstrap工具的方法
2017/10/19 Javascript
微信小程序中使用自定义图标(阿里icon)的方法
2018/08/20 Javascript
Vue监听一个数组id是否与另一个数组id相同的方法
2018/09/26 Javascript
JavaScript显式数据类型转换详解
2019/03/18 Javascript
vue 实现根据data中的属性值来设置不同的样式
2020/08/04 Javascript
python判断一个数是否能被另一个整数整除的实例
2018/12/12 Python
PyQt5下拉式复选框QComboCheckBox的实例
2019/06/25 Python
pytorch 预训练层的使用方法
2019/08/20 Python
tensorflow 报错unitialized value的解决方法
2020/02/06 Python
利用jupyter网页版本进行python函数查询方式
2020/04/14 Python
Python如何实现远程方法调用
2020/08/07 Python
Python中BeautifulSoup通过查找Id获取元素信息
2020/12/07 Python
css3.0 图形构成实例练习一
2013/03/19 HTML / CSS
CSS实现半透明边框与多重边框的场景分析
2019/11/13 HTML / CSS
比利时香水网上商店:NOTINO
2018/03/28 全球购物
Shop Apotheke瑞士:您的健康与美容网上商店
2019/10/09 全球购物
美团网旗下网上订餐平台:美团外卖
2020/03/05 全球购物
大学自我鉴定
2013/12/20 职场文书
学校安全检查制度
2014/01/27 职场文书
导游词400字
2015/02/13 职场文书
简单的辞职信模板
2015/05/12 职场文书
2015初中生物教研组工作总结
2015/07/21 职场文书
Python实现自动玩连连看的脚本分享
2022/04/04 Python
Nginx 常用配置
2022/05/15 Servers