官术网_书友最值得收藏!

5.2 重點API的剖析

通過前文的示例代碼對比,相信有讀者已經迫不及待地想要學習Lodash.js的使用方法了,本節就先來看看它的API的構成吧。Lodash.js的API大致可以分為如下幾個大類。

  • 數組(Array)操作類
  • 集合(Collection)操作類
  • 函數(Function)操作類
  • 語言(Lang)工具類
  • 數學(Math)類
  • 序列(Sequence)類
  • 字符串(String)操作類
  • 常用工具(Util)類

詳細的使用方法直接查看官方文檔就可以了,大部分方法并不復雜,我們需要做的只是熟悉它們,然后使用它們,感興趣的話還可以自己試著實現一下,然后再看看官方的源碼是如何實現的,你會發現其中有非常多既有趣又實用的知識。本節將重點介紹官方文檔沒有詳細說明但開發者需要了解的那部分內容。

1. Collection

Lodash.js中的許多方法是基于特定的數據類型來分組的,細心的讀者會發現針對數組和對象的方法分別放在了Array組和Object組中,還有一部分方法在歸類時劃分到了Collection組中,可是JavaScript中并沒有Collection這個數據類型,我們要如何使用這類方法呢?事實上,劃分到Collection這個類別中的方法,其數據集既可以是Array類型,也可以是Object類型,這樣劃分的目的是為一些抽象行為提供統一的名稱。在JavaScript語言中,數組實例和對象實例的原型鏈分別如下:

//數組實例的原型鏈
[].__proto__ = Array.prototype
[].__proto__.__proto__ = Object.prototype
//對象實例的原型鏈
({}).__proto__ = Object.prototype

從原型鏈中我們很容易看出,數組實例是可以使用對象方法的,因為它的原型鏈上有Object.prototype對象,但是對象實例也會因為類似的原因而無法直接使用數組方法,這就使得許多看起來非常相似的邏輯在實現細節上卻有很大的差別。例如,典型的遍歷、查找、映射、排序、聚合等操作,在數組中我們可以直接使用對應的forEach find、map、sort、reduce等方法,但是在處理對象類型時,幾乎只能通過一遍又一遍地在邏輯的外層包裹上“for...in...”和“hasOwnProperty”等代碼段來實現。而Lodash.js為這些數組和對象都會用到的方法(不僅僅是Array.prototype上的方法)提供了不同的實現并將其封裝起來了,這就為開發者提供了更加友好且一致的API。

2. 不可變數據

初級開發者常常會分不清一個原生方法是會直接改變源數據還是會生成新的數據,為了在開發中避免自己編寫的邏輯影響不想修改的數據,許多初級開發者會多次調用深備份方法對數據集進行備份,這種思路是正確的,但是多次深備份所帶來的的性能損失卻不容忽視。使用Lodash.js就可以避免出現這種混亂。

在函數的實現上,Lodash.js會遵循“不修改原數據”的原則,這就意味著在你將一個數據集傳入某個方法后,期望的結果總是會以函數返回值的形式傳遞回來,如果另一個變量標識符指向了原來的數據集,那么它不會受到任何影響。這樣的設計提供了額外的一致性保障,你可以非常確切地知道自己得到的是否是新的數據集。操作嵌套類型的數據時,需要格外小心,最穩妥的辦法就是在使用前測試一下某個方法的真實表現。

3. 高階函數

Function操作類中的方法所涉及的幾乎都是高階函數的知識,也是前端面試必考的知識點——閉包的應用,即使不使用高階函數,雖然一樣也可以實現這些方法的功能,但這樣做的代價就是將一些本來只有自己使用的狀態變量提升到了更外層的作用域中,這樣一來,不僅無法實現私有變量的隔離,而且也很容易帶來更多的干擾。

例如,你正在使用Vue框架開發一個組件,在一個鼠標移動事件中,你希望通過函數節流(throttle)的功能來限制一個高性能消耗的事件處理函數的觸發頻率,如果不使用高階函數,那么你就必須將記錄每次移動事件時間戳的變量掛載在“data”上(即一個更外層的作用域),盡管它的消費者只有鼠標移動事件的回調函數,但是它的掛載方式卻使得這個變量可以被其他函數訪問或修改。從組件設計的角度來看,這種做法違背了基本的封裝原則,增加了不必要的干擾。如果使用高階函數來實現,那么這個記錄時間戳的變量就會被封裝在高階函數內部,如果你使用的是Lodash.js提供的“_.throttle()”方法,那么主邏輯代碼中甚至連這個變量都不會出現,組件中的主邏輯代碼也會因此變得更加清晰,這樣的結構也更符合“高內聚,低耦合”的開發原則。

4. 數據分離和邏輯聚合

“數據分離”是指將數據從邏輯中剝離出來,“邏輯聚合”是指將主邏輯的代碼盡量聚合在一起,而把它們的實現細節封裝起來。一個復雜的業務邏輯可能會按步驟調用大量的方法,初學者極有可能會寫出耦合度超高的巨型函數,又或者將其拆解為若干個步驟,在每個步驟的結尾處調用下一個步驟,這樣的代碼維護和調試起來非常困難,過多的細節會讓你難以聚焦主要的業務邏輯代碼。在Lodash.js中,我們既可以使用類似于jQuery的鏈式調用風格來組裝業務邏輯的多個步驟,也可以使用類似于函數式編程中管道(pipe)的風格,無論如何,將細節封裝起來,將重要的信息聚合在一起,都可以讓代碼變得更清晰和易維護。

在Lodash.js中,將數據集傳入“_.chain()”方法中,可以開啟一段鏈式調用風格的邏輯,示例[1]代碼如下:

var users = [
    { 'user': 'barney',  'age': 36 },
    { 'user': 'fred',    'age': 40 },
    { 'user': 'pebbles', 'age': 1 }
];
var youngest = _
    .chain(users)
    .sortBy('age')
    .map(function(o) {
        return o.user + ' is ' + o.age;
    })
    .head()
    .value();
//輸出的結果為:'pebbles is 1'

也可以引用函數式編程(Functional Programming)風格的Lodash.js,并使用“_.pipe”方法將函數按照執行步驟組合在一起,示例代碼如下:

const _ = require('lodash/fp');
const youngest = _.pipe([_.sortBy('age'),_.map(o=>o.user+' is '+o.age),_.head]);
console.log(youngest(users));
//輸出的結果為: 'pebbles is 1'

函數式編程和面向對象編程是兩種不同的編程范式,面向對象編程通常更適合用來描述抽象實體之間的聯系,而函數式編程則在數據加工的任務中顯得更靈活簡潔。無論選擇哪種邏輯聚合風格,我們幾乎都可以通過編寫出更少的應用層代碼來表達業務邏輯的主線,搞清楚程序中“做了哪些事”,而不是“做了哪些事以及分別是怎么做的”,另一方面,業務邏輯的聚合也減少了中間變量的使用,使代碼更加精簡。


[1]示例來自Lodash.js官方文檔。

主站蜘蛛池模板: 日土县| 睢宁县| 汨罗市| 新建县| 宣威市| 元江| 华容县| 井研县| 乐东| 运城市| 池州市| 禹州市| 绥阳县| 桂东县| 镇远县| 金平| 易门县| 灵山县| 金堂县| 麻阳| 武功县| 库尔勒市| 哈尔滨市| 盐山县| 上栗县| 额济纳旗| 武川县| 健康| 长宁县| 通化市| 嫩江县| 承德市| 北海市| 大名县| 平南县| 探索| 宜城市| 本溪市| 东阿县| 扶风县| 古田县|