浅析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编写trim()函数及正则表达式的运用
Oct 24 Javascript
jquery分页插件jpaginate在IE中不兼容问题
Apr 22 Javascript
详解Angular中$cacheFactory缓存的使用
Aug 19 Javascript
关于JavaScript数组你所不知道的3件事
Aug 24 Javascript
jquery仿ps颜色拾取功能
Mar 08 Javascript
AngularJS双向绑定和依赖反转实例详解
Apr 15 Javascript
JavaScript实现二叉树定义、遍历及查找的方法详解
Dec 20 Javascript
vue项目实战总结篇
Feb 11 Javascript
解决Vue打包之后文件路径出错的问题
Mar 06 Javascript
element-ui 设置菜单栏展开的方法
Aug 22 Javascript
深入理解Angularjs 脏值检测
Oct 12 Javascript
使用VueCli3+TypeScript+Vuex一步步构建todoList的方法
Jul 25 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学习之PHP表达式
2006/10/09 PHP
PHP中判断变量为空的几种方法分享
2013/08/26 PHP
php第一次无法获取cookie问题处理
2014/12/15 PHP
再谈IE中Flash控件的自动激活 ObjectWrap
2007/03/09 Javascript
Javascript生成json的函数代码(可以用php的json_decode解码)
2012/06/11 Javascript
Js 获取Gridview选中行的内容操作步骤
2013/02/05 Javascript
js图片延迟加载的实现方法及思路
2013/07/22 Javascript
最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
2017/08/16 Javascript
webstorm+vue初始化项目的方法
2018/10/18 Javascript
js实现图片放大并跟随鼠标移动特效
2019/01/18 Javascript
jquery操作checkbox的常用方法总结【附测试源码下载】
2019/06/10 jQuery
微信小程序下拉加载和上拉刷新两种实现方法详解
2019/09/05 Javascript
使用pkg打包ThinkJS项目的方法步骤
2019/12/30 Javascript
javascript设计模式 ? 桥接模式原理与应用实例分析
2020/04/13 Javascript
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
2020/09/08 Javascript
[49:31]TFT vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
Python脚本实现虾米网签到功能
2016/04/12 Python
Python使用wxPython实现计算器
2018/01/30 Python
python线程join方法原理解析
2020/02/11 Python
Python paramiko 模块浅谈与SSH主要功能模拟解析
2020/02/29 Python
Kears 使用:通过回调函数保存最佳准确率下的模型操作
2020/06/17 Python
Python判断变量是否是None写法代码实例
2020/10/09 Python
纯CSS实现预加载动画效果
2017/09/06 HTML / CSS
CSS3教程(3):border-color网页边框色彩
2009/04/02 HTML / CSS
关于老式浏览器兼容HTML5和CSS3的问题
2016/06/01 HTML / CSS
Hunkemöller西班牙:欧洲最大的内衣连锁店
2018/08/15 全球购物
迪卡侬(Decathlon)加拿大官网:源自法国的运动专业超市
2020/11/22 全球购物
培训楼经理岗位责任制
2014/02/10 职场文书
竞选班长自荐书范文
2014/03/09 职场文书
乡镇综治宣传月活动总结
2014/07/02 职场文书
小学课外阅读总结
2014/07/09 职场文书
小学运动会报道稿
2014/10/04 职场文书
绿色校园广播稿
2014/10/13 职场文书
安装工程师岗位职责
2015/02/13 职场文书
英文慰问信
2015/02/14 职场文书
Windows Server 2022 超融合部署(图文教程)
2022/06/25 Servers