Underscore.js

Underscore.js

Underscore一個JavaScript實用庫,提供了一整套函數式編程的實用功能,可是沒有擴展任何JavaScript內置對象。javascript

它彌補了部分jQuery沒有實現的功能,同時又是Backbone.js必不可少的部分。css

Underscore還能夠被使用在Node.js運行環境html

API地址:http://www.css88.com/doc/underscore/java

下載: git clone https://github.com/jashkenas/underscorejquery

分類知識點

Collections

Collections是集合,指那些由單個元素組成,可以使用下標操做的數據類型的統稱,好比Array,Object,String等,從underscore的源碼來看,這一類函數只用到最基本的[]運算符和for循環,以及對由此構成的新方法的組合運用。這一類方法有:each、map、reduce、find等。git

Arrays

相對Collections的鳥槍而言,Arrays有了小鋼炮,可以使用Array內置的方法,好比slice。underscore的Arrays方法不只適用於Array類型,也適用於String和Array-like類型的對象。這一類方法有:union、intersection、difference、indexOf等。github

簡單介紹一下Array-like,顧名思義,就是像Array而不是Array的一種數據類型,它的特色是可以經過數字下標(0、一、2 ...)訪問,有length屬性,可是不能使用Array的內置方法。這類裏面比較常見的是arguments,就是函數的參數列表,Object.getOwnPropertyNames(arguments)的返回值除了參數列表外,還有length和callee兩個屬性。那麼對這種類型若是要想用Array的內置方法怎麼辦呢?能夠經過數組泛化來調用,有兩種方式:編程

1  Array. method (obj, function(){});
2 Array.prototype. method .call(obj, function(){}); 

 

在underscore裏面主要用了call的方式。既然提到了call,就再對call進行一下解釋。數組

在javascript中call和apply經常使用於實現繼承機制,兩者很相似,只有第二個參數略有差別。調用 call() 方法時,調用者是須要執行的函數對象第一個參數就是要執行函數中的 this變量,後面的參數都會做爲參數傳遞給要執行函數。例如:瀏覽器

var me = {
    name: "Alex",
    City: "Beijing",
};
function sayHello(comments) {console.log("Hi, " + this.name + comments)};
sayHello.call(me, ", you are great!");

執行後會打印出「Hi, Alex, you are great!」。

再好比

a.func().call(b) 

就至關於b繼承了a, 結果就是b調用了a的func()方法。

在Collections部分的源碼中常常要對Array或Array-like類型與Object類型分開來處理,用到了一個技巧

if (obj.length === +obj.length) {}

對於前者而言,返回爲true,而Object沒有length屬性,obj.length返回的是undefined,"+"是將其餘類型轉化爲數字或者NaN,等同於Number(obj.length),+undefined的結果是NaN,所以整個表達式返回false。這裏有一個知識點是null, false, undefined, NaN的關係。

Functions

underscore中Functions部分提供了一些與Function相關的函數,在javascript裏,函數自己也是一種變量類型,函數名只是指向函數對象的引用值,跟其餘變量有同樣的行爲。

函數自己也有length屬性,表明函數可以接受的參數個數;相對而言,arguments.length表示的是實際傳入的參數個數,兩者數量能夠不一樣。若是實參比形參要少,沒有的參數被賦值爲undefined。

Functions部分的函數有些使用了閉包(closure),用於保存狀態或者做爲緩存,好比once、after和memoize等。閉包是javascript的一大特點,原理是根據鏈式做用域(chain scope)的原則,上級變量對下級可見。若是在對象或者函數內部再定義函數,而內部的函數使用了上級的變量,當將這個函數被做爲返回值時,返回的函數就成爲閉包,而上級的變量由於仍舊被使用所以會一直保存在內存中。

這一類的函數包括:bind、memorize、debounce、throttle等。

Objects

Objects部分最複雜的一個函數是內部用的equal,可能也是整個underscore最複雜的一個,這裏最重要的知識點就是javascript的判等,注意引用類型不能直接使用「==」或「===」,須要使用迭代函數轉化成原始類型進行比較。underscore的isEqual函數與javascript的徹底等同(===)或相等(==)不太同樣,更符合人的直覺。根據源碼簡單總結一下規則,有順序,前面的規則沒有匹配才匹配後面的:

  1. 若是兩個變量徹底等同,且不爲0,則返回true。若是是0的話,要保證符號同樣(0和-0認爲不相等)
  2. 若是兩個變量都是null或都是undefined,返回true
  3. 若是兩個變量的類型不同,返回false,類型使用Object.prototype.toString.call()來得到
  4. 對於String類型,原始類型和包裝類型,只要值同樣,認爲相等
    「a」 isEqual new String("a")
  5. 兩個變量都是NaN返回true,判斷是不是NaN依據Object.prototype.toString.call()爲[object Number],且a!=+a;其餘Number類型依據egal原則判斷
  6. 對於Date和Boolean類型轉化爲原始的數字類型進行比較
  7. RegExp類型要比較source、global、multiline和ignoreCase幾個屬性
  8. 函數變量認爲不相等(引用指向同一個函數的狀況已經在第一條規則排除了)
  9. 對於數組和普通對象類型,基於上面的規則遞歸判斷,全部子對象都相等才返回true

Objects部分的函數包括:keys、values、has、isEqual以及一對isType判斷類型的函數。

Utility

Utility部分根據比較有用的是生成隨機數和生成ID,還包括增長自定義函數的mixin,轉義html的escape,以及一個簡單的html模板函數。

OO方式

除了使用函數風格的underscore外,還可使用面向對象的方式,在這種方式下,underscore支持鏈式調用。

重要方法介紹:

1 Underscore對象封裝

1 //定義一個內置對象
2 var data={
3 name:'ccy'
4 }
5 //經過_()方法將對象建立爲一個Underscore對象
6 //underData對象的原型中包含了Underscore重定義的全部方法ta,能夠任意使用
7 var underData=_(data);
8 //經過value()方法得到原生數據,即data
9 underData.value();

2鏈式操做

Underscore支持鏈式操做,但你須要先調用chain()方法進行聲明:

    var arr = [10, 20, 30];  
    _(arr)  
        .chain()  
        .map(function(item){ return item++; })  
        .first()  
        .value();

 

若是調用了chain()方法,Underscore會將所調用的方法封裝在一個閉包內,並將返回值封裝爲一個Underscore對象並返回:

  // 這是Underscore中實現鏈式操做的關鍵函數,它將返回值封裝爲一個新的Underscore對象,並再次調用chain()方法,爲方法鏈中的下一個函數提供支持。  
    var result = function(obj, chain) {  
        return chain ? _(obj).chain() : obj;  
    }

3擴展自定義方法

咱們能夠經過mixin()方法輕鬆地向Underscore中擴展自定義方法

這些方法被追加到Underscore的原型對象中,全部建立的Underscore對象均可以使用這些方法,它們享有和其它方法一樣的環境。

 _.mixin({  
        method1: function(object) {  
            // todo  
        },  
        method2: function(arr) {  
            // todo  
        },  
        method3: function(fn) {  
            // todo  
        }  
    });

4遍歷集合

each()和map()方法是最經常使用用到的兩個方法,它們用於迭代一個集合(數組或對象),並依次處理集合中的每個元素,

map()方法與each()方法的做用、參數相同,但它會將每次迭代函數返回的結果記錄到一個新的數組並返回。

    var arr = [1, 2, 3];
     
    _(arr).map(function(item, i) {
    arr[i] = item + 1;
    });
     
    var obj = {
    first : 1,
    second : 2
    }
     
    _(obj).each(function(value, key) {
    return obj[key] = value + 1;
    });

5函數節流

函數節流是指控制一個函數的執行頻率或間隔(就像控制水流的閘門同樣),Underscore提供了debounce()和throttle()兩個方法用於函數節流。

  • debounce()方法關注函數執行的間隔,即函數兩次的調用時間不能小於指定時間。
  • throttle()方法更關注函數的執行頻率,即在指定頻率內函數只會被調用一次。

需求:當用戶在文本框輸入搜索條件時,自動查詢匹配的關鍵字並提示給用戶

1     <input type="text" id="search" name="search" />  
2     <script type="text/javascript">  
3         var query = _(function() {  
4             // 在這裏進行查詢操做  
5         }).debounce(200);  
6      
7         $('#search').bind('keypress', query);  
8     </script>

需求:當用戶拖動瀏覽器滾動條時,調用服務器接口檢查是否有新的內容

    <script type="text/javascript">  
        var query = _(function() {  
            // 在這裏進行查詢操做  
        }).throttle(500);  
     
        $(window).bind('scroll', query);  
    </script>

6模板解析

Underscore提供了一個輕量級的模板解析函數,它能夠幫助咱們有效地組織頁面結構和邏輯。

    <!-- 用於顯示渲染後的標籤 -->
    <ul id="element"></ul>
     
    <!-- 定義模板,將模板內容放到一個script標籤中 -->
    <script type="text/template" id="tpl">
    <% for(var i = 0; i < list.length; i++) { %>
    <% var item = list[i] %>
    <li>
    <span><%=item.firstName%> <%=item.lastName%></span>
    <span><%-item.city%></span>
    </li>
    <% } %>
    </script>
    <script type="text/javascript" src="underscore/underscore-min.js"></script>
    <script type="text/javascript">
    // 獲取渲染元素和模板內容
    var element = $('#element'),
    tpl = $('#tpl').html();
     
    // 建立數據, 這些數據多是你從服務器獲取的
    var data = {
    list: [
    {firstName: '<a href="#">Zhang</a>', lastName: 'San', city: 'Shanghai'},
    {firstName: 'Li', lastName: 'Si', city: '<a href="#">Beijing</a>'},
    {firstName: 'Wang', lastName: 'Wu', city: 'Guangzhou'},
    {firstName: 'Zhao', lastName: 'Liu', city: 'Shenzhen'}
    ]
    }
     
    // 解析模板, 返回解析後的內容
    var html = _.template(tpl, data);
    // 將解析後的內容填充到渲染元素
    element.html(html);
    </script>

在本例中,咱們將模板內容放到一個<script>標籤中,你可能已經注意到標籤的type是text/template而不是text/javascript,由於它沒法做爲JavaScript腳本直接運行。

我也建議你將模板內容放在<script>中,由於若是你將它們寫在一個<div>或其它標籤中,它們可能會被添加到DOM樹中進行解析(即便你隱藏了這個標籤也沒法避免)。

在本例中,咱們將模板內容和須要填充的數據傳遞給template方法,它會按如下順序進行處理:

  • 將模板內容解析爲可執行的JavaScript(解析模板標籤)
  • 經過with語句將解析後的JavaScript做用域修改成咱們傳遞的數據對象,這使咱們可以直接在模板中經過變量形式訪問數據對象的屬性
  • 執行解析後的JavaScript(將數據填充到模板)
  • 返回執行後的結果

咱們常常會遇到一種狀況:屢次調用template方法將數據渲染到同一個模板。

假設咱們有一個分頁列表,列表中的每一條數據都經過模板渲染,當用戶進入下一頁,咱們會獲取下一頁的數據並從新渲染,實際上每次渲染的模板都是同一個,但剛纔描述的template全部處理過程總會被執行。

其實Underscore的template方法提供了一種更高效的調用方式,咱們將上面代碼中的最後兩句修改成:

    // 解析模板, 返回解析後的內容
    var render = _.template(tpl);
    var html = render(data);
    // 將解析後的內容填充到渲染元素
    element.html(html);

你會發現細微的差異:咱們在調用template方法時只傳遞了模板內容,而沒有傳遞數據,此時template方法會解析模板內容,生成解析後的可執行JavaScript代碼,並返回一個函數,而函數體就是解析後的JavaScript,所以當咱們調用該函數渲染數據時,就省去了模板解析的動做。

你應該將返回的函數存儲起來(就像我將它存儲在render變量中同樣),再經過調用該函數來渲染數據,特別是在同一個模板可能會被屢次渲染的狀況下,這樣作能提升執行效率

_.template模板函數只能解析3種模板標籤:

<%  %>:用於包含JavaScript代碼,這些代碼將在渲染數據時被執行。

<%= %>:用於輸出數據,能夠是一個變量、某個對象的屬性、或函數調用(將輸出函數的返回值)。

<%- %>:用於輸出數據,同時會將數據中包含的HTML字符轉換爲實體形式(例如它會將雙引號轉換爲&quot;形式),用於避免XSS攻擊。

當咱們但願將數據中的HTML做爲文本顯示出來時,經常會使用<%- %>標籤。

Underscore還容許你修改這3種標籤的形式,若是咱們想使用{% %}、{%= %}、{%- %}做爲標籤,能夠經過修改templateSettings來實現,就像這樣:

    _.templateSettings = {
    evaluate : /\{%([\s\S]+?)\%\}/g, interpolate : /\{%=([\s\S]+?)\%\}/g, escape : /\{%-([\s\S]+?)%\}/g }
相關文章
相關標籤/搜索