浅析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实现的GridView即表头固定表体有滚动条且可滚动
Feb 19 Javascript
jquery实现文本框数量加减功能的例子分享
May 10 Javascript
JavaScript使用循环和分割来替换和删除元素实例
Oct 13 Javascript
jQuery实现多张图片上传预览(不经过后端处理)
Apr 29 jQuery
jquery replace方法去空格
May 08 jQuery
基于es6三点运算符的使用方法(实例讲解)
Oct 12 Javascript
详解如何制作并发布一个vue的组件的npm包
Nov 10 Javascript
微信上传视频文件提示(推荐)
Nov 22 Javascript
React 使用recharts实现散点地图的示例代码
Dec 07 Javascript
微信小程序textarea层级过高(盖住其他元素)问题的解决办法
Mar 04 Javascript
Vue在 Nuxt.js 中重定向 404 页面的方法
Apr 23 Javascript
Vue的路由及路由钩子函数的实现
Jul 02 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 创建文件(文件夹)以及目录操作代码
2010/03/04 PHP
PHP实现取得HTTP请求的原文
2014/08/18 PHP
Thinkphp实现短信验证注册功能
2016/10/18 PHP
thinkPHP简单实现多个子查询语句的方法
2016/12/05 PHP
JavaScript中各种编码解码函数的区别和注意事项
2010/08/19 Javascript
解析window.open的使用方法总结
2013/06/19 Javascript
使用javascript实现有效时间的控制,并显示将要过期的时间
2014/01/02 Javascript
javascript快速排序算法详解
2014/09/17 Javascript
浅谈JavaScript中promise的使用
2017/01/11 Javascript
Nodejs中使用phantom将html转为pdf或图片格式的方法
2017/09/18 NodeJs
小程序扫描普通链接二维码跳转小程序指定界面方法
2019/05/07 Javascript
详解如何在Javascript和Sass之间共享变量
2019/11/13 Javascript
vue 实现锚点功能操作
2020/08/10 Javascript
解决vue下载后台传过来的乱码流的问题
2020/12/05 Vue.js
[50:02]完美世界DOTA2联赛PWL S2 Magma vs FTD 第三场 11.29
2020/12/03 DOTA
探索Python3.4中新引入的asyncio模块
2015/04/08 Python
编写Python脚本把sqlAlchemy对象转换成dict的教程
2015/05/29 Python
python操作ssh实现服务器日志下载的方法
2015/06/03 Python
Python素数检测实例分析
2015/06/15 Python
python删除列表内容
2015/08/04 Python
python 实现对数据集的归一化的方法(0-1之间)
2018/07/17 Python
Python3几个常见问题的处理方法
2019/02/26 Python
python GUI库图形界面开发之PyQt5单选按钮控件QRadioButton详细使用方法与实例
2020/02/28 Python
Python实现捕获异常发生的文件和具体行数
2020/04/25 Python
selenium如何定位span元素的实现
2021/01/13 Python
暇步士官网:Hush Puppies
2016/09/22 全球购物
泰国第一的化妆品网站:Konvy
2018/02/25 全球购物
俄罗斯三星品牌商店:GalaxyStore
2020/11/04 全球购物
CSMA/CD介质访问控制协议
2015/11/17 面试题
机械工程系毕业生求职信
2013/09/27 职场文书
实习生自荐信范文分享
2013/11/27 职场文书
学生夜不归宿检讨书
2014/09/23 职场文书
春风化雨观后感
2015/06/11 职场文书
python3 hdf5文件 遍历代码
2021/05/19 Python
Python实现猜拳与猜数字游戏的方法详解
2022/04/06 Python
Python识别花卉种类鉴定网络热门植物并自动整理分类
2022/04/08 Python