淺析Prototype的模板類 Template
更新時(shí)間:2011年12月07日 00:30:46 作者:
淺析Prototype的模板類 Template,需要的朋友可以參考下。
用過Prototype的人都知道,里面有個(gè)類叫做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'
這么挺方便的,所以下面就簡(jiǎn)單的分析一下實(shí)現(xiàn)原理,也算是源碼解讀的一個(gè)筆記。
我們先看一下一般需求里面會(huì)用到的一些情況,還是用上面的例子,先決定我們的形式,替換的部分是形如#{what}的內(nèi)容,其中what是一個(gè)object對(duì)象的一個(gè)關(guān)鍵字。
現(xiàn)在有個(gè)問題就是,如果object是一個(gè)嵌套的對(duì)象,我們?cè)撛趺刺鎿Q?
即:
<script type="text/javascript">
var object = {
what : {
name : 'Swallows'
},
how : 'return'
}
</script>
最開始的#{what}肯定不能滿足要求,所以我們硬性規(guī)定,如果要實(shí)現(xiàn)嵌套對(duì)象的替換,寫作#{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>
源碼里面有個(gè)正則var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;就是用來實(shí)現(xiàn)這個(gè)目的的。依次類推,任何深層次的嵌套都可以實(shí)現(xiàn)。
要做替換,我們最核心的就是要有一個(gè)替換字符的方法,源碼里面用的是gsub,下面給出一個(gè)gsub的最簡(jiǎn)版本:
<script type="text/javascript">
function gsub(str,pattern, replacement){
var result = '',
source = str,
match;
//下面的每一次匹配都是分成三段來操作的
//相當(dāng)于 $` $& $'
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>
這個(gè)調(diào)用方法和原理跟我前面涉及的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是匹配規(guī)則,object就是最初例子中的object;現(xiàn)在重點(diǎn)剩下evaluate方法的實(shí)現(xiàn):
直接調(diào)用gsub方法,主要是gsub中replacement的方法了。
可能出現(xiàn)的情況,
第一、object是一個(gè)空對(duì)象,那么我們直接刪除需要替換的部分
比如
var str = '#{what} may have gone, but there is a time of #{how}';
var object = {}
那么就直接返回' may have gone, but there is a time of '
第二、轉(zhuǎn)義部分直接保留
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中(.*?)匹配的部分
//示例:
//對(duì)于 s#{what} 來說before='s',content='#{what}',expr='what'
//第一、object是一個(gè)空對(duì)象,那么我們直接刪除需要替換的部分
if(object == null){
return (match[1] + '');
}
//第二、轉(zhuǎn)義部分直接保留
if (before == '\\'){
return content;
}
//除了上面的兩種情況,下面是正常的替換流程。
var ctx = object;
//下面這個(gè)正則我在前面說過,是為了匹配嵌套對(duì)象的??醋詈竺娴?\.|\[|$)是為了確定有沒有嵌套的子對(duì)象匹配。
//#{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]是一個(gè)字符串,那么下面一步就可以收工了,如果ctx[comp]還是一個(gè)對(duì)象,那么抱歉,加班繼續(xù)干吧。
if (null == ctx || '' == match[3]){//需要替換的不是object的嵌套子對(duì)象,那么就直接中斷循環(huán)。替換成功,下班。
break;
}
//下面的僅僅是為了剝離出來關(guān)鍵字,其他操作用在循環(huán)里面再來一次。
if('[' == match[3]){
expr = expr.substring(match[1].length)
}else{
expr = expr.substring(match[0].length)
}
match = pattern.exec(expr);
}
return before + ctx;
});
};
當(dāng)然,源碼中并沒有這么簡(jiǎn)單,還參雜了一寫檢測(cè)和判斷,比如替換str的非法字符,處理replacement為字符串的情況等等。具體可以去查看源碼。
可能有些復(fù)雜的就是處理replacement為字符串的情況,gsub和Template有一個(gè)嵌套調(diào)用的情況,不過意思都一樣,最后丟回一個(gè)函數(shù)就可以了。
復(fù)制代碼 代碼如下:
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'
這么挺方便的,所以下面就簡(jiǎn)單的分析一下實(shí)現(xiàn)原理,也算是源碼解讀的一個(gè)筆記。
我們先看一下一般需求里面會(huì)用到的一些情況,還是用上面的例子,先決定我們的形式,替換的部分是形如#{what}的內(nèi)容,其中what是一個(gè)object對(duì)象的一個(gè)關(guān)鍵字。
現(xiàn)在有個(gè)問題就是,如果object是一個(gè)嵌套的對(duì)象,我們?cè)撛趺刺鎿Q?
即:
復(fù)制代碼 代碼如下:
<script type="text/javascript">
var object = {
what : {
name : 'Swallows'
},
how : 'return'
}
</script>
最開始的#{what}肯定不能滿足要求,所以我們硬性規(guī)定,如果要實(shí)現(xiàn)嵌套對(duì)象的替換,寫作#{what.name}或者#{what[name]}。
所以最開始的例子可以寫作:
復(fù)制代碼 代碼如下:
<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>
源碼里面有個(gè)正則var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;就是用來實(shí)現(xiàn)這個(gè)目的的。依次類推,任何深層次的嵌套都可以實(shí)現(xiàn)。
要做替換,我們最核心的就是要有一個(gè)替換字符的方法,源碼里面用的是gsub,下面給出一個(gè)gsub的最簡(jiǎn)版本:
復(fù)制代碼 代碼如下:
<script type="text/javascript">
function gsub(str,pattern, replacement){
var result = '',
source = str,
match;
//下面的每一次匹配都是分成三段來操作的
//相當(dāng)于 $` $& $'
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>
這個(gè)調(diào)用方法和原理跟我前面涉及的replace類似,基本可以互換。http://www.cnblogs.com/xesam/archive/2011/12/05/2276783.html
復(fù)制代碼 代碼如下:
<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來,基本要求:有類有方法
復(fù)制代碼 代碼如下:
<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是匹配規(guī)則,object就是最初例子中的object;現(xiàn)在重點(diǎn)剩下evaluate方法的實(shí)現(xiàn):
直接調(diào)用gsub方法,主要是gsub中replacement的方法了。
可能出現(xiàn)的情況,
第一、object是一個(gè)空對(duì)象,那么我們直接刪除需要替換的部分
比如
var str = '#{what} may have gone, but there is a time of #{how}';
var object = {}
那么就直接返回' may have gone, but there is a time of '
第二、轉(zhuǎn)義部分直接保留
復(fù)制代碼 代碼如下:
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}';
這些情況在代碼中都要處理,具體代碼如下
復(fù)制代碼 代碼如下:
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中(.*?)匹配的部分
//示例:
//對(duì)于 s#{what} 來說before='s',content='#{what}',expr='what'
//第一、object是一個(gè)空對(duì)象,那么我們直接刪除需要替換的部分
if(object == null){
return (match[1] + '');
}
//第二、轉(zhuǎn)義部分直接保留
if (before == '\\'){
return content;
}
//除了上面的兩種情況,下面是正常的替換流程。
var ctx = object;
//下面這個(gè)正則我在前面說過,是為了匹配嵌套對(duì)象的??醋詈竺娴?\.|\[|$)是為了確定有沒有嵌套的子對(duì)象匹配。
//#{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]是一個(gè)字符串,那么下面一步就可以收工了,如果ctx[comp]還是一個(gè)對(duì)象,那么抱歉,加班繼續(xù)干吧。
if (null == ctx || '' == match[3]){//需要替換的不是object的嵌套子對(duì)象,那么就直接中斷循環(huán)。替換成功,下班。
break;
}
//下面的僅僅是為了剝離出來關(guān)鍵字,其他操作用在循環(huán)里面再來一次。
if('[' == match[3]){
expr = expr.substring(match[1].length)
}else{
expr = expr.substring(match[0].length)
}
match = pattern.exec(expr);
}
return before + ctx;
});
};
當(dāng)然,源碼中并沒有這么簡(jiǎn)單,還參雜了一寫檢測(cè)和判斷,比如替換str的非法字符,處理replacement為字符串的情況等等。具體可以去查看源碼。
可能有些復(fù)雜的就是處理replacement為字符串的情況,gsub和Template有一個(gè)嵌套調(diào)用的情況,不過意思都一樣,最后丟回一個(gè)函數(shù)就可以了。
相關(guān)文章
prototype Element學(xué)習(xí)筆記(Element篇三)
上一篇把Element的所函數(shù)都梳理了一遍,下面總結(jié)一下這些函數(shù)的功能,畢竟函數(shù)太多,不分門別類一下還是沒有底。2008-10-10使用prototype.js 的時(shí)候應(yīng)該特別注意的幾個(gè)問題.
使用prototype.js 的時(shí)候應(yīng)該特別注意的幾個(gè)問題....2007-04-04不錯(cuò)的一篇關(guān)于javascript-prototype繼承
不錯(cuò)的一篇關(guān)于javascript-prototype繼承...2007-08-08基礎(chǔ)的prototype.js常用函數(shù)及其用法
基礎(chǔ)的prototype.js常用函數(shù)及其用法...2007-03-03Prototype Class對(duì)象學(xué)習(xí)
Prototype’s object for class-based OOP.prototype OOP編程的基礎(chǔ),詳細(xì)說明一下源碼2009-07-07動(dòng)態(tài)表格Table類的實(shí)現(xiàn)
文件名:Table.js本文件依賴于 prototype.js,prototype_ext.js,Lib.js,DataBinder.js這些文件請(qǐng)參看我的其它文章2009-08-08Prototype1.5 rc2版指南最后一篇之Position
Prototype1.5 rc2版指南最后一篇之Position...2007-01-01