浅析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 相关文章推荐
jQuery 前的按键判断代码
Mar 19 Javascript
js获取当前select 元素值的代码
Apr 19 Javascript
javascript 循环读取JSON数据的代码
Jul 17 Javascript
ECMAScript 5严格模式(Strict Mode)介绍
Mar 02 Javascript
Jquery中基本选择器用法实例详解
May 18 Javascript
详解Sea.js中Module.exports和exports的区别
Feb 12 Javascript
vue v-model表单控件绑定详解
May 17 Javascript
JavaScript基于遍历操作实现对象深拷贝功能示例
Mar 05 Javascript
bootstrap Table实现合并相同行
Jul 19 Javascript
vue从零实现一个消息通知组件的方法详解
Mar 16 Javascript
记一次用ts+vuecli4重构项目的实现
May 21 Javascript
JS实现多选框的操作
Jun 24 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
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
解析获取优酷视频真实下载地址的PHP源代码
2013/06/26 PHP
Laravel重写用户登录简单示例
2016/10/08 PHP
解决出现SoapFault (looks like we got no XML document)的问题
2017/06/24 PHP
PHP内存溢出优化代码详解
2021/02/26 PHP
用Javascript 和 CSS 实现脚注(Footnote)效果
2009/09/09 Javascript
DOM 高级编程
2015/05/06 Javascript
jquery实现的伪分页效果代码
2015/10/29 Javascript
基于jquery实现动态竖向柱状条特效
2016/02/12 Javascript
JS组件Bootstrap实现弹出框效果代码
2016/04/26 Javascript
JavaScript_object基础入门(必看篇)
2016/06/13 Javascript
AngularJs  Creating Services详解及示例代码
2016/09/02 Javascript
从零学习node.js之express入门(六)
2017/02/25 Javascript
jQuery实现锚点向下平滑滚动特效示例
2017/08/29 jQuery
javascript  删除select中的所有option的实例
2017/09/17 Javascript
原生JS实现的自动轮播图功能详解
2018/12/28 Javascript
详解如何用webpack4从零开始构建react开发环境
2019/01/27 Javascript
详解React项目中碰到的IE问题
2019/03/14 Javascript
js实现AI五子棋人机大战
2020/05/28 Javascript
vue+springboot图片上传和显示的示例代码
2020/02/14 Javascript
浅谈django不使用restframework自定义接口与使用的区别
2020/07/15 Python
pandas apply多线程实现代码
2020/08/17 Python
python跨文件使用全局变量的实现
2020/11/17 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
2021/02/04 Python
Python3+Django get/post请求实现教程详解
2021/02/16 Python
Python中Qslider控件实操详解
2021/02/20 Python
英国知名的皮手套品牌:Dents
2016/11/13 全球购物
意大利折扣和优惠券网站:Groupalia
2019/10/09 全球购物
意大利辅助药品、药物和补品在线销售:FarmaEurope
2020/04/29 全球购物
德国苹果商店:MacTrade
2020/05/18 全球购物
请介绍一下Ant
2016/07/22 面试题
竞选副班长演讲稿
2014/04/24 职场文书
大学新闻系应届生求职信
2014/06/02 职场文书
初中生旷课检讨书范文
2014/10/06 职场文书
2015初一年级组工作总结
2015/07/24 职场文书
Java中Dijkstra(迪杰斯特拉)算法
2022/05/20 Java/Android