zoukankan      html  css  js  c++  java
  • 浅析Prototype的模板类Template

    用过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有一个嵌套调用的情况,不过意思都一样,最后丢回一个函数就可以了。


    转载请注明来自小西山子【http://www.cnblogs.com/xesam/
    本文地址:http://www.cnblogs.com/xesam/archive/2011/12/05/2277260.html










  • 相关阅读:
    python string
    python md5使用例子
    Linux 与 Win32 部分API对照表
    使用Doxygen生成libevent document
    CRC32校验算法以及余式表生成
    SQLServer中查询时显示行号的方法
    C#上传文件图片怎么判断格式
    登录注册 分页存储过程及系统存储过程
    SQL Server 批量插入数据的两种方法
    sqlserver得到行号
  • 原文地址:https://www.cnblogs.com/xesam/p/2277260.html
Copyright © 2011-2022 走看看