浅析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 OOP包机制,类创建的方法定义
Nov 02 Javascript
jquery 实现表单验证功能代码(简洁)
Jul 03 Javascript
得到form下的所有的input的js代码
Nov 07 Javascript
使用typeof方法判断undefined类型
Sep 09 Javascript
JavaScript版的TwoQueues缓存模型
Dec 29 Javascript
javascript中Date()函数在各浏览器中的显示效果
Jun 18 Javascript
简介alert()与console.log()的不同
Aug 26 Javascript
一个简易的js图片轮播效果
Jul 22 Javascript
JS温故而知新之变量提升和时间死区
Jan 27 Javascript
解决在layer.open中使用时间控件laydate失败的问题
Sep 11 Javascript
node.js中 mysql 增删改查操作及async,await处理实例分析
Feb 11 Javascript
vue.js 输入框输入值自动过滤特殊字符替换中问标点操作
Aug 31 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产生随机数的两种方法实例代码 输出随机IP
2011/04/08 PHP
关于Sphinx创建全文检索的索引介绍
2013/06/25 PHP
PHP输出一个等腰三角形的方法
2015/05/12 PHP
php使用ftp实现文件上传与下载功能
2017/07/21 PHP
PHP基于递归实现的约瑟夫环算法示例
2017/08/27 PHP
phpstudy隐藏index.php的方法
2020/09/21 PHP
基础的prototype.js常用函数及其用法
2007/03/10 Javascript
二级域名或跨域共享Cookies的实现方法
2008/08/07 Javascript
关于文本框的一些限制控制总结~~
2010/04/15 Javascript
解析jquery中的ajax缓存问题
2013/12/19 Javascript
js图片处理示例代码
2014/05/12 Javascript
jquery easyui使用心得
2014/07/07 Javascript
JavaScript获取伪元素(Pseudo-Element)属性的方法技巧
2015/03/13 Javascript
js数组去重的方法汇总
2015/07/29 Javascript
Jquery跨浏览器文本复制插件Zero Clipboard的使用方法
2016/02/28 Javascript
jQuery学习之DOM节点的插入方法总结
2017/01/22 Javascript
JavaScript实现更换背景图片
2019/10/18 Javascript
基于Python的XSS测试工具XSStrike使用方法
2017/07/29 Python
Python面向对象实现一个对象调用另一个对象操作示例
2019/04/08 Python
Python当中的array数组对象实例详解
2019/06/12 Python
python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例
2019/06/17 Python
简单了解python协程的相关知识
2019/08/31 Python
python中自带的三个装饰器的实现
2019/11/08 Python
python groupby 函数 as_index详解
2019/12/16 Python
Pytorch.nn.conv2d 过程验证方式(单,多通道卷积过程)
2020/01/03 Python
python爬取王者荣耀全皮肤的简单实现代码
2020/01/31 Python
Python读取xlsx数据生成图标代码实例
2020/08/12 Python
Python实现EM算法实例代码
2020/10/04 Python
全球知名的婚恋交友网站:Match.com
2017/01/05 全球购物
计算机毕业大学生推荐信
2013/12/01 职场文书
法律专业学生的自我评价
2014/02/07 职场文书
银行竞聘演讲稿
2014/05/16 职场文书
无刑事犯罪记录证明范本
2014/09/29 职场文书
导游词之江南周庄
2019/12/06 职场文书
《黑岩★★射手 DAWN FALL》BD发售宣传CM公开
2022/04/04 日漫
SQL Server表分区降低运维和维护成本
2022/04/08 SQL Server