输出变量的界定符
interpolate : /<%=([\s\S]+?)%>/g,
// 需要将HTML输出为字符串(将特殊符号转换为字符串形式)的界定符
escape : /<%-([\s\S]+?)%>/g
};
var noMatch = /.^/;
// escapes对象记录了需要进行相互换转的特殊符号与字符串形式的对应关系, 在两者进行相互转换时作为索引使用
// 首先根据字符串形式定义特殊字符
var escapes = {
'\\' : '\\',
"'" : "'",
'r' : '\r',
'n' : '\n',
't' : '\t',
'u2028' : '\u2028',
'u2029' : '\u2029'
};
// 遍历所有特殊字符字符串, 并以特殊字符作为key记录字符串形式
for(var p in escapes)
escapes[escapes[p]] = p;
// 定义模板中需要替换的特殊符号, 包含反斜杠, 单引号, 回车符, 换行符, 制表符, 行分隔符, 段落分隔符
// 在将字符串中的特殊符号转换为字符串形式时使用
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// 在将字符串形式的特殊符号进行反转(替换)时使用
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
// 反转字符串中的特殊符号
// 在模板中涉及到需要执行的JavaScript源码, 需要进行特殊符号反转, 否则如果以HTML实体或字符串形式出现, 会抛出语法错误
var unescape = function(code) {
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};
// Underscore模板解析方法, 用于将数据填充到一个模板字符串中
// 模板解析流程:
// 1. 将模板中的特殊符号转换为字符串
// 2. 解析escape形式标签, 将内容解析为HTML实体
// 3. 解析interpolate形式标签, 输出变量
// 4. 解析evaluate形式标签, 创建可执行的JavaScript代码
// 5. 生成一个处理函数, 该函数在得到数据后可直接填充到模板并返回填充后的字符串
// 6. 根据参数返回填充后的字符串或处理函数的句柄
// -------------------
// 在模板体内, 可通过argments获取2个参数, 分别为填充数据(名称为obj)和Underscore对象(名称为_)
_.template = function(text, data, settings) {
// 模板配置, 如果没有指定配置项, 则使用templateSettings中指定的配置项
settings = _.defaults(settings || {}, _.templateSettings);
// 开始将模板解析为可执行源码
var source = "__p+='" + text.replace(escaper, function(match) {
// 将特殊符号转移为字符串形式
return '\\' + escapes[match];
}).replace(settings.escape || noMatch, function(match, code) {
// 解析escape形式标签 <%- %>, 将变量中包含的HTML通过_.escape函数转换为HTML实体
return "'+\n_.escape(" + unescape(code) + ")+\n'";
}).replace(settings.interpolate || noMatch, function(match, code) {
// 解析interpolate形式标签 <%= %>, 将模板内容作为一个变量与其它字符串连接起来, 则会作为一个变量输出
return "'+\n(" + unescape(code) + ")+\n'";
}).replace(settings.evaluate || noMatch, function(match, code) {
// 解析evaluate形式标签 <% %>, evaluate标签中存储了需要执行的JavaScript代码, 这里结束当前的字符串拼接, 并在新的一行作为JavaScript语法执行, 并将后面的内容再次作为字符串的开始, 因此evaluate标签内的JavaScript代码就能被正常执行
return "';\n" + unescape(code) + "\n;__p+='";
}) + "';\n";
if(!settings.variable)
source = 'with(obj||{}){\n' + source + '}\n';
source = "var __p='';" + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n";
// 创建一个函数, 将源码作为函数执行体, 将obj和Underscore作为参数传递给该函数
var render = new Function(settings.variable || 'obj', '_', source);
// 如果指定了模板的填充数据, 则替换模板内容, 并返回替换后的结果
if(data)
return render(data, _);
// 如果没有指定填充数据, 则返回一个函数, 该函数用于将接收到的数据替换到模板
// 如果在程序中会多次填充相同模板, 那么在第一次调用时建议不指定填充数据, 在获得处理函数的引用后, 再直接调用会提高运行效率
var template = function(data) {
return render.call(this, data, _);
};
// 将创建的源码字符串添加到函数对象中, 一般用于调试和测试
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
// 没有指定填充数据的情况下, 返回处理函数句柄
return template;
};
// 支持Underscore对象的方法链操作, 可参考 wrapper.prototype.chain
_.chain = function(obj) {
return _(obj).chain();
};
// Underscore对象封装相关方法
// ---------------
// 创建一个包装器, 将一些原始数据进行包装
// 所有的undersocre对象, 内部均通过wrapper函数进行构造和封装
// Underscore与wrapper的内部关系:
// -内部定义变量_, 将Underscore相关的方法添加到_, 这样就可以支持函数式的调用, 如_.bind()
// -内部定义wrapper类, 将_的原型对象指向wrapper类的原型
// -将Underscore相关的方法添加到wrapper原型, 创建的_对象就具备了Underscore的方法
// -将Array.prototype相关方法添加到wrapper原型, 创建的_对象就具备了Array.prototype中的方法
// -new _()时实际创建并返回了一个wrapper()对象, 并将原始数组存储到_wrapped变量, 并将原始值作为第一个参数调用对应方法
var wrapper = function(obj) {
// 原始数据存放在包装对象的_wrapped属性中
this._wrapped = obj;
};
// 将Underscore的原型对象指向wrapper的原型, 因此通过像wrapper原型中添加方法, Underscore对象也会具备同样的方法
_.prototype = wrapper.prototype;
// 返回一个对象, 如果当前Underscore调用了chain()方法(即_chain属性为true), 则返回一个被包装的Underscore对象, 否则返回对象本身
// result函数
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com