浅析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 相关文章推荐
Javascript 学习书 推荐
Jun 13 Javascript
JavaScript 学习笔记 Black.Caffeine 09.11.28
Nov 30 Javascript
javascript document.execCommand() 常用解析
Dec 14 Javascript
动态加载图片路径 保持JavaScript控件的相对独立性
Sep 06 Javascript
JS 面向对象之神奇的prototype
Feb 26 Javascript
Javascript创建自定义对象 创建Object实例添加属性和方法
Jun 04 Javascript
Jquery EasyUI中弹出确认对话框以及加载效果示例代码
Feb 13 Javascript
JS数组去掉重复数据只保留一条的实现代码
Aug 11 Javascript
JS中IP地址与整数相互转换的实现代码
Apr 10 Javascript
ionic中的$ionicPlatform.ready事件中的通用设置
Jun 11 Javascript
JavaScript Event Loop相关原理解析
Jun 10 Javascript
JavaScript函数柯里化实现原理及过程
Dec 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
无线电波是什么?它是怎样传输的?
2021/03/01 无线电
mysql_fetch_assoc和mysql_fetch_row的功能加起来就是mysql_fetch_array
2007/01/15 PHP
PHP 文件上传进度条的两种实现方法的代码
2007/11/25 PHP
php使用递归计算文件夹大小
2014/12/24 PHP
php自定义urlencode,urldecode函数实例
2015/03/24 PHP
基于PHP实现简单的随机抽奖小程序
2016/01/05 PHP
使用ucenter实现多站点同步登录的讲解
2019/03/21 PHP
Array栈方法和队列方法的特点说明
2014/01/24 Javascript
JS中操作JSON总结
2020/12/06 Javascript
Jquery 点击按钮自动高亮实现原理及代码
2014/04/25 Javascript
jquery阻止后续事件只执行第一个事件
2014/07/24 Javascript
javascript中scrollTop详解
2015/04/13 Javascript
两款JS脚本判断手机浏览器类型跳转WAP手机网站
2015/10/16 Javascript
JS实现slide文字框缩放伸展效果代码
2015/11/05 Javascript
基于BootStrap的图片轮播效果展示实例代码
2016/05/23 Javascript
5种JavaScript脚本加载的方式
2017/01/16 Javascript
浅谈es6 javascript的map数据结构
2017/12/14 Javascript
nodejs中Express与Koa2对比分析
2018/02/06 NodeJs
使用Vue制作图片轮播组件思路详解
2018/03/21 Javascript
Vue组件之极简的地址选择器的实现
2018/05/31 Javascript
浅谈Vue初学之props的驼峰命名
2018/07/19 Javascript
Vue指令之 v-cloak、v-text、v-html实例详解
2019/08/08 Javascript
Echarts地图添加引导线效果(labelLine)
2019/09/30 Javascript
JS实现扫码枪扫描二维码功能
2020/01/03 Javascript
Vue向后台传数组数据,springboot接收vue传的数组数据实例
2020/11/12 Javascript
对python中执行DOS命令的3种方法总结
2018/05/12 Python
python3中的md5加密实例
2018/05/29 Python
python统计字符串中字母出现次数代码实例
2020/03/02 Python
PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译
2020/04/15 Python
Django封装交互接口代码
2020/07/12 Python
详解python tkinter 图片插入问题
2020/09/03 Python
pytest fixtures装饰器的使用和如何控制用例的执行顺序
2021/01/28 Python
Nike爱尔兰官方网站:Nike.com (IE)
2018/03/12 全球购物
思想专业自荐信范文
2013/12/25 职场文书
保安岗位职责
2014/02/21 职场文书
使用 Apache 反向代理的设置技巧
2022/01/18 Servers