輸出變量的界定符
interpolate : /<%=([\s\S]+?)%>/g,
// 需要將HTML輸出為字符串(將特殊符號轉(zhuǎn)換為字符串形式)的界定符
escape : /<%-([\s\S]+?)%>/g
};
var noMatch = /.^/;
// escapes對象記錄了需要進行相互換轉(zhuǎn)的特殊符號與字符串形式的對應關系, 在兩者進行相互轉(zhuǎn)換時作為索引使用
// 首先根據(jù)字符串形式定義特殊字符
var escapes = {
'\\' : '\\',
"'" : "'",
'r' : '\r',
'n' : '\n',
't' : '\t',
'u2028' : '\u2028',
'u2029' : '\u2029'
};
// 遍歷所有特殊字符字符串, 并以特殊字符作為key記錄字符串形式
for(var p in escapes)
escapes[escapes[p]] = p;
// 定義模板中需要替換的特殊符號, 包含反斜杠, 單引號, 回車符, 換行符, 制表符, 行分隔符, 段落分隔符
// 在將字符串中的特殊符號轉(zhuǎn)換為字符串形式時使用
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// 在將字符串形式的特殊符號進行反轉(zhuǎn)(替換)時使用
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
// 反轉(zhuǎn)字符串中的特殊符號
// 在模板中涉及到需要執(zhí)行的JavaScript源碼, 需要進行特殊符號反轉(zhuǎn), 否則如果以HTML實體或字符串形式出現(xiàn), 會拋出語法錯誤
var unescape = function(code) {
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};
// Underscore模板解析方法, 用于將數(shù)據(jù)填充到一個模板字符串中
// 模板解析流程:
// 1. 將模板中的特殊符號轉(zhuǎn)換為字符串
// 2. 解析escape形式標簽, 將內(nèi)容解析為HTML實體
// 3. 解析interpolate形式標簽, 輸出變量
// 4. 解析evaluate形式標簽, 創(chuàng)建可執(zhí)行的JavaScript代碼
// 5. 生成一個處理函數(shù), 該函數(shù)在得到數(shù)據(jù)后可直接填充到模板并返回填充后的字符串
// 6. 根據(jù)參數(shù)返回填充后的字符串或處理函數(shù)的句柄
// -------------------
// 在模板體內(nèi), 可通過argments獲取2個參數(shù), 分別為填充數(shù)據(jù)(名稱為obj)和Underscore對象(名稱為_)
_.template = function(text, data, settings) {
// 模板配置, 如果沒有指定配置項, 則使用templateSettings中指定的配置項
settings = _.defaults(settings || {}, _.templateSettings);
// 開始將模板解析為可執(zhí)行源碼
var source = "__p+='" + text.replace(escaper, function(match) {
// 將特殊符號轉(zhuǎn)移為字符串形式
return '\\' + escapes[match];
}).replace(settings.escape || noMatch, function(match, code) {
// 解析escape形式標簽 <%- %>, 將變量中包含的HTML通過_.escape函數(shù)轉(zhuǎn)換為HTML實體
return "'+\n_.escape(" + unescape(code) + ")+\n'";
}).replace(settings.interpolate || noMatch, function(match, code) {
// 解析interpolate形式標簽 <%= %>, 將模板內(nèi)容作為一個變量與其它字符串連接起來, 則會作為一個變量輸出
return "'+\n(" + unescape(code) + ")+\n'";
}).replace(settings.evaluate || noMatch, function(match, code) {
// 解析evaluate形式標簽 <% %>, evaluate標簽中存儲了需要執(zhí)行的JavaScript代碼, 這里結束當前的字符串拼接, 并在新的一行作為JavaScript語法執(zhí)行, 并將后面的內(nèi)容再次作為字符串的開始, 因此evaluate標簽內(nèi)的JavaScript代碼就能被正常執(zhí)行
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";
// 創(chuàng)建一個函數(shù), 將源碼作為函數(shù)執(zhí)行體, 將obj和Underscore作為參數(shù)傳遞給該函數(shù)
var render = new Function(settings.variable || 'obj', '_', source);
// 如果指定了模板的填充數(shù)據(jù), 則替換模板內(nèi)容, 并返回替換后的結果
if(data)
return render(data, _);
// 如果沒有指定填充數(shù)據(jù), 則返回一個函數(shù), 該函數(shù)用于將接收到的數(shù)據(jù)替換到模板
// 如果在程序中會多次填充相同模板, 那么在第一次調(diào)用時建議不指定填充數(shù)據(jù), 在獲得處理函數(shù)的引用后, 再直接調(diào)用會提高運行效率
var template = function(data) {
return render.call(this, data, _);
};
// 將創(chuàng)建的源碼字符串添加到函數(shù)對象中, 一般用于調(diào)試和測試
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
// 沒有指定填充數(shù)據(jù)的情況下, 返回處理函數(shù)句柄
return template;
};
// 支持Underscore對象的方法鏈操作, 可參考 wrapper.prototype.chain
_.chain = function(obj) {
return _(obj).chain();
};
// Underscore對象封裝相關方法
// ---------------
// 創(chuàng)建一個包裝器, 將一些原始數(shù)據(jù)進行包裝
// 所有的undersocre對象, 內(nèi)部均通過wrapper函數(shù)進行構造和封裝
// Underscore與wrapper的內(nèi)部關系:
// -內(nèi)部定義變量_, 將Underscore相關的方法添加到_, 這樣就可以支持函數(shù)式的調(diào)用, 如_.bind()
// -內(nèi)部定義wrapper類, 將_的原型對象指向wrapper類的原型
// -將Underscore相關的方法添加到wrapper原型, 創(chuàng)建的_對象就具備了Underscore的方法
// -將Array.prototype相關方法添加到wrapper原型, 創(chuàng)建的_對象就具備了Array.prototype中的方法
// -new _()時實際創(chuàng)建并返回了一個wrapper()對象, 并將原始數(shù)組存儲到_wrapped變量, 并將原始值作為第一個參數(shù)調(diào)用對應方法
var wrapper = function(obj) {
// 原始數(shù)據(jù)存放在包裝對象的_wrapped屬性中
this._wrapped = obj;
};
// 將Underscore的原型對象指向wrapper的原型, 因此通過像wrapper原型中添加方法, Underscore對象也會具備同樣的方法
_.prototype = wrapper.prototype;
// 返回一個對象, 如果當前Underscore調(diào)用了chain()方法(即_chain屬性為true), 則返回一個被包裝的Underscore對象, 否則返回對象本身
// result函數(shù)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com