摘要:第一版我們來嘗試實現(xiàn)第一版第一版為了驗證是否有用文件文件完整的可以查看示例一在這里我們使用了,實際上在文章中使用的是構造函數(shù)。構造函數(shù)創(chuàng)建一個新的對象。
前言
underscore 提供了模板引擎的功能,舉個例子:
var tpl = "hello: <%= name %>"; var compiled = _.template(tpl); compiled({name: "Kevin"}); // "hello: Kevin"
感覺好像沒有什么強大的地方,再來舉個例子:
在 HTML 文件中:
JavaScript 文件中:
var container = document.getElementById("user_tmpl"); var data = { users: [ { "name": "Kevin", "url": "http://localhost" }, { "name": "Daisy", "url": "http://localhost" }, { "name": "Kelly", "url": "http://localhost" } ] } var precompile = _.template(document.getElementById("user_tmpl").innerHTML); var html = precompile(data); container.innerHTML = html;
效果為:
那么該如何實現(xiàn)這樣一個 _.template 函數(shù)呢?
實現(xiàn)思路underscore 的 template 函數(shù)參考了 jQuery 的作者 John Resig 在 2008 年發(fā)表的一篇文章 JavaScript Micro-Templating,我們先從這篇文章的思路出發(fā),思考一下如何寫一個簡單的模板引擎。
依然是以這段模板字符串為例:
<%for ( var i = 0; i < users.length; i++ ) { %>
John Resig 的思路是將這段代碼轉(zhuǎn)換為這樣一段程序:
// 模擬數(shù)據(jù) var users = [{"name": "Kevin", "url": "http://localhost"}]; var p = []; for (var i = 0; i < users.length; i++) { p.push("
我們注意,模板其實是一段字符串,我們怎么根據(jù)一段字符串生成一段代碼呢?很容易就想到用 eval,那我們就先用 eval 吧。
然后我們會發(fā)現(xiàn),為了轉(zhuǎn)換成這樣一段代碼,我們需要將<%xxx%>轉(zhuǎn)換為 xxx,其實就是去掉包裹的符號,還要將 <%=xxx%>轉(zhuǎn)化成 p.push(xxx),這些都可以用正則實現(xiàn),但是我們還需要寫 p.push("
那我們換個思路,依然是用正則,但是我們
將 %> 替換成 p.push("
將 <% 替換成 ");
將 <%=xxx%> 替換成 ");p.push(xxx);p.push("
我們來舉個例子:
<%for ( var i = 0; i < users.length; i++ ) { %>
按照這個替換規(guī)則會被替換為:
");for ( var i = 0; i < users.length; i++ ) { p.push("
這樣肯定會報錯,畢竟代碼都沒有寫全,我們在首和尾加上部分代碼,變成:
// 添加的首部代碼 var p = []; p.push(" ");for ( var i = 0; i < users.length; i++ ) { p.push("
我們整理下這段代碼:
var p = []; p.push(""); for ( var i = 0; i < users.length; i++ ) { p.push("
恰好可以實現(xiàn)這個功能,不過還要注意一點,要將換行符替換成空格,防止解析成代碼的時候報錯,不過在這里為了方便理解原理,就只在代碼里實現(xiàn)。
第一版我們來嘗試實現(xiàn)第一版:
// 第一版 function tmpl(str, data) { var str = document.getElementById(str).innerHTML; var string = "var p = []; p.push("" + str .replace(/[ ]/g, "") .replace(/<%=(.*?)%>/g, "");p.push($1);p.push("") .replace(/<%/g, "");") .replace(/%>/g,"p.push("") + "");" eval(string) return p.join(""); };
為了驗證是否有用:
HTML 文件:
JavaScript 文件:
var users = [ { "name": "Byron", "url": "http://localhost" }, { "name": "Casper", "url": "http://localhost" }, { "name": "Frank", "url": "http://localhost" } ] tmpl("user_tmpl", users)
完整的 Demo 可以查看 template 示例一
Function在這里我們使用了 eval ,實際上 John Resig 在文章中使用的是 Function 構造函數(shù)。
Function 構造函數(shù)創(chuàng)建一個新的 Function 對象。 在 JavaScript 中, 每個函數(shù)實際上都是一個 Function 對象。
使用方法為:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
arg1, arg2, ... argN 表示函數(shù)用到的參數(shù),functionBody 表示一個含有包括函數(shù)定義的 JavaScript 語句的字符串。
舉個例子:
var adder = new Function("a", "b", "return a + b"); adder(2, 6); // 8
那么 John Resig 到底是如何實現(xiàn)的呢?
第二版使用 Function 構造函數(shù):
// 第二版 function tmpl(str, data) { var str = document.getElementById(str).innerHTML; var fn = new Function("obj", "var p = []; p.push("" + str .replace(/[ ]/g, "") .replace(/<%=(.*?)%>/g, "");p.push($1);p.push("") .replace(/<%/g, "");") .replace(/%>/g,"p.push("") + "");return p.join("");"); return fn(data); };
使用方法依然跟第一版相同,具體 Demo 可以查看 template 示例二
不過值得注意的是:其實 tmpl 函數(shù)沒有必要傳入 data 參數(shù),也沒有必要在最后 return 的時候,傳入 data 參數(shù),即使你把這兩個參數(shù)都去掉,代碼還是可以正常執(zhí)行的。
這是因為:
使用Function構造器生成的函數(shù),并不會在創(chuàng)建它們的上下文中創(chuàng)建閉包;它們一般在全局作用域中被創(chuàng)建。當運行這些函數(shù)的時候,它們只能訪問自己的本地變量和全局變量,不能訪問Function構造器被調(diào)用生成的上下文的作用域。這和使用帶有函數(shù)表達式代碼的 eval 不同。
這里之所以依然傳入了 data 參數(shù),是為了下一版做準備。
with現(xiàn)在有一個小問題,就是實際上我們傳入的數(shù)據(jù)結構可能比較復雜,比如:
var data = { status: 200, name: "kevin", friends: [...] }
如果我們將這個數(shù)據(jù)結構傳入 tmpl 函數(shù)中,在模板字符串中,如果要用到某個數(shù)據(jù),總是需要使用 data.name、data.friends 的形式來獲取,麻煩就麻煩在我想直接使用 name、friends 等變量,而不是繁瑣的使用 data. 來獲取。
這又該如何實現(xiàn)的呢?答案是 with。
with 語句可以擴展一個語句的作用域鏈(scope chain)。當需要多次訪問一個對象的時候,可以使用 with 做簡化。比如:
var hostName = location.hostname; var url = location.href; // 使用 with with(location){ var hostname = hostname; var url = href; }
function Person(){ this.name = "Kevin"; this.age = "18"; } var person = new Person(); with(person) { console.log("my name is " + name + ", age is " + age + ".") } // my name is Kevin, age is 18.
最后:不建議使用 with 語句,因為它可能是混淆錯誤和兼容性問題的根源,除此之外,也會造成性能低下
第三版使用 with ,我們再寫一版代碼:
// 第三版 function tmpl(str, data) { var str = document.getElementById(str).innerHTML; var fn = new Function("obj", // 其實就是這里多添加了一句 with(obj){...} "var p = []; with(obj){p.push("" + str .replace(/[ ]/g, "") .replace(/<%=(.*?)%>/g, "");p.push($1);p.push("") .replace(/<%/g, "");") .replace(/%>/g,"p.push("") + "");}return p.join("");"); return fn(data); };
具體 Demo 可以查看 template 示例三
第四版如果我們的模板不變,數(shù)據(jù)卻發(fā)生了變化,如果使用我們的之前寫的 tmpl 函數(shù),每次都會 new Function,這其實是沒有必要的,如果我們能在使用 tmpl 的時候,返回一個函數(shù),然后使用該函數(shù),傳入不同的數(shù)據(jù),只根據(jù)數(shù)據(jù)不同渲染不同的 html 字符串,就可以避免這種無謂的損失。
// 第四版 function tmpl(str, data) { var str = document.getElementById(str).innerHTML; var fn = new Function("obj", "var p = []; with(obj){p.push("" + str .replace(/[ ]/g, "") .replace(/<%=(.*?)%>/g, "");p.push($1);p.push("") .replace(/<%/g, "");") .replace(/%>/g,"p.push("") + "");}return p.join("");"); var template = function(data) { return fn.call(this, data) } return template; }; // 使用時 var compiled = tmpl("user_tmpl"); results.innerHTML = compiled(data);
具體 Demo 可以查看 template 示例四
下期預告至此,我們已經(jīng)跟著 jQuery 的作者 John Resig 實現(xiàn)了一個簡單的模板引擎,雖然 underscore 基于這個思路實現(xiàn),但是功能強大,相對的,代碼也更加復雜一下,下一篇,我們一起去分析 underscore 的 template 函數(shù)實現(xiàn)。
underscore 系列underscore 系列目錄地址:https://github.com/mqyqingfeng/Blog。
underscore 系列預計寫八篇左右,重點介紹 underscore 中的代碼架構、鏈式調(diào)用、內(nèi)部函數(shù)、模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫出自己的 undercore。
如果有錯誤或者不嚴謹?shù)牡胤剑垊毡亟o予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對作者也是一種鼓勵。
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/92271.html
摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個系列后,再跟著其他系列的文章接著學習。如何閱讀我在寫系列的時候,被問的最多的問題就是該怎么閱讀源碼我想簡單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個系列再見啦 前言 別名:《underscore 系列 8 篇正式完結!》 介紹 underscore 系列是我寫的第三個系列,前兩個系列分別是 JavaScript 深入系列、...
摘要:前言本篇接著上篇系列之實現(xiàn)一個模板引擎上。字符串中的每個字符均可由一個轉(zhuǎn)義序列表示。在中,有四個字符被認為是行終結符,其他的折行字符都會被視為空白。 前言 本篇接著上篇 underscore 系列之實現(xiàn)一個模板引擎(上)。 鑒于本篇涉及的知識點太多,我們先來介紹下會用到的知識點。 反斜杠的作用 var txt = We are the so-called Vikings from th...
摘要:前端模板的出現(xiàn)使得前后端分離成為可能。總結本文簡單介紹了模板引擎在前后端的使用,下文我們回到,重點分析下的使用方式以及源碼原理。樓主對于模板引擎的認識比較淺顯,有不正之處希望指出感謝 前言 這篇文章本來不打算寫的,實話說樓主對前端模板的認識還處在非常初級的階段,但是為了整個 源碼解讀系列 的完整性,在深入 Underscore _.template 方法源碼后,覺得還是有必要記下此文,...
摘要:與最后,使用我們的寫的函數(shù)重寫下函數(shù)系列系列目錄地址。系列預計寫八篇左右,重點介紹中的代碼架構鏈式調(diào)用內(nèi)部函數(shù)模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫出自己的。如果有錯誤或者不嚴謹?shù)牡胤剑垊毡亟o予指正,十分感謝。 partial 在《 JavaScript 專題之偏函數(shù)》中,我們寫了一個 partial 函數(shù),用來固定函數(shù)的部分參數(shù),實現(xiàn)代碼如下: // 這是文章中的第一版 fu...
摘要:前言提供了函數(shù),用于轉(zhuǎn)義字符串,替換和字符為字符實體。如果希望正確地顯示預留字符,我們必須在源代碼中使用字符實體。字符實體有兩種形式。轉(zhuǎn)義我們的應對方式就是將取得的值中的特殊字符轉(zhuǎn)為字符實體。 前言 underscore 提供了 _.escape 函數(shù),用于轉(zhuǎn)義 HTML 字符串,替換 &, , , , 和 ` 字符為字符實體。 _.escape(Curly, Larry & Moe)...
閱讀 3245·2023-04-26 01:31
閱讀 1892·2023-04-25 22:08
閱讀 3430·2021-09-01 11:42
閱讀 2823·2019-08-30 12:58
閱讀 2165·2019-08-29 18:31
閱讀 2429·2019-08-29 17:18
閱讀 3064·2019-08-29 13:01
閱讀 2551·2019-08-28 18:22