繼續前面的內容,前文咱們提到了不少方法的講解,其實到這裏就已經差很少了,由於大部分代碼其實都是套路,一些基礎函數再靈活變化就能夠組成不少實用的功能。html
_.sortBy = function(obj, iteratee, context) { var index = 0; iteratee = cb(iteratee, context); return _.pluck(_.map(obj, function(value, key, list) { return { value: value, index: index++, criteria: iteratee(value, key, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); };
_.sortBy
,顧名思義這是一個對數組進行排序處理的函數,在原生 JAVASCRIPT 中 sort() 的詳情可參考 Array.prototype.sort()、TypedArray.prototype.sort()。_.sortBy
接收三個參數分別爲 obj、iteratee 回調和 context,其中 iteratee 與 context 是可選參數。
當傳入值只有 obj 時,應該限定 obj 類型爲數組且值爲 Number,爲何呢,這裏涉及到 JAVASCRIPT 對數字字符串的比較的問題了,JAVASCRIPT 在進行字符串比較的時候遵循的是二進制與運算,也就是說並非數字 length 越長就會大於 length 小的。舉個栗子:linux
_.sortBy([1, 2, 3, 4, 5, 6, 8, 7, 11, 13]); [1, 2, 3, 4, 5, 6, 7, 8, 11, 13] _.sortBy(['1', '2', '3', '4', '5', '6', '8', '7', '11', '13']); ["1", "11", "13", "2", "3", "4", "5", "6", "7", "8"]
同窗們都很聰明,不用我在說了,言歸正傳,當只有 obj 一個值且值爲 Number,那麼默認從左到右從小到大排序,爲何呢,我看下代碼,在 _.pluck
中代碼只作了一件事,就是整理數據,當沒有 iteratee 的時候執行 cb
函數裏的 if (value == null) return _.identity;
也就是至關於默認 iteratee function 爲 _.identity
即 return obj,因此 _.map
中回調的 criteria 值即 value。有點繞口,代碼起開(假定只有 obj 一個參數):數組
_.sortBy = function(obj) { var index = 0; return _.pluck(_.map(obj, function(value, key, list) { return { value: value, index: index++, criteria: (function(value, key, list) { return value; })(value, key, list); }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); };
這樣看上去就直白好多。整理完數據以後就是 arr.sort([compareFunction])
進行排序,這裏不說了。當傳入參數有 iteratee 回調的時候,依舊老套路優化回調,而後根據回調函數裏面的設定決定 criteria 參數值,criteria 參數是 arr.sort([compareFunction])
進行排序的關鍵標識,so必定要是 Number才行。網絡
var group = function(behavior, partition) { return function(obj, iteratee, context) { var result = partition ? [[], []] : {}; iteratee = cb(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; };
group
是一個內部函數,我以爲它最特別在於將回調稱之爲一個 behavior,爲何呢,由於雖然 behavior function 只能被動接受 value, index, obj
三個參數進行數值運算,但做者巧妙的用它結合 group 包裝出 _.groupBy
、_.indexBy
、_.countBy
、_.partition
四個函數,在實際開發中咱們處理數據時可能須要各類適用場景的工具,那麼把如何函數寫好寫活呢,group 給了我很大的啓發,言歸正傳,group 的 behavior 回調是在外部定義,源碼到這裏並不知道 behavior 是什麼東西,因此先一帶而過。數據結構
_.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; });
_.groupBy
官網定義把一個集合分組爲多個集合,經過 iterator 返回的結果進行分組. 若是 iterator 是一個字符串而不是函數, 那麼將使用 iterator 做爲各元素的屬性名來對比進行分組.
。併發
———————— 頹廢的分割線 ————————ide
從昨天到今天狀態不佳,昏天黑地的看了兩天電影,看到最後都不知道本身在看什麼,我須要吐槽一下小米路由器,因爲我是 linux 系統,做爲 deiban 死忠黨來講一臺不到兩千元的臺式機想要連接無線網絡,折騰的時間和金錢都不如再填個路由器作中繼划算,因而我買了這貨 小米路由器,它在路由器模式下還算能夠,一但調整到中繼模式,這徹底就是一個入坑的神展開,啪啪啪的隨時無間歇性斷網沒商量,莫名其妙的就連不上網了,即便鏈接上網絡網速都不如無線的通常有木有,在過去的一段時間裏我有 N 次想把這款路由器摔在地上(額,或者摔在牆上),但願你們不要吐槽我兩千塊都不到的臺式主力機,價錢雖然 lower 了點,但性能絕對夠用,對於 mac 黨們我很但願你們轉粉,雖然我也有 mac 可是我平均開機數目大約在 1/(1~2個月)。函數
寫到這裏目測大約水了一百多個文字,繼續前天的講解 ╮(╯Д╰)╭ 。工具
———————— END ————————性能
官網的意思是什麼呢,假如我有一個 obj,那麼我可使用 _.groupBy
函數將這個 obj 經過其內部值的某個屬性進行分類,而這個屬性值的判斷也能夠經過回調進行擴展斷言。那麼當 iteratee 爲 null 時,_.groupBy
默認使用前面的 group
函數中的 cb 函數的 if (value == null) return _.identity;
處理 iteratee 爲空的狀況,我來簡化一下 _.groupBy
:
_.groupBy = function(obj) { var result = partition ? [[], []] : {}; _.each(obj, function(value, index) { var key = value; if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }) return result; }
這樣理解是否是淺顯不少呢,設置 result 空數組,而後 _.each
遍歷 obj,滿滿的都是套路有木有,惟一亮點的地方就是 if 判斷是根據 _.has
函數肯定 result 中是否已經存在 key-value。可是這裏面還有一個更深的套路,那就是做者沒有對 obj 做進一步處理,因此 _.groupBy
函數只能適用於 Array,舉個栗子:
_.groupBy(['one', 'two', 'three']); {"one":["one"],"two":["two"],"three":["three"]} _.groupBy([{a:'one'}, {b:'two'}, {c:'three'}]); {"[object Object]":[{"a":"one"},{"b":"two"},{"c":"three"}]}
而後咱們再說一下 _.groupBy
參數有第二個參數的狀況,這裏能夠看出 cb 函數的重要性,它對 iteratee 的類型狀況作了細緻的判斷和處理,咱們前面能夠知道 cb 函數除了 Null、Function、Object 意外的類型都用 _.property
處理,即 生成獲取屬性值的函數,那麼咱們傳參爲數組呢,see ↓↓↓
_.groupBy(['one', 'two', 'three'],[1,2,3]) {"false":["one","two","three"]}
也就是說做者雖然大才,可是並無對超出範圍的值類型作進一步的處理,也就是說 iteratee 的可選值類型只能爲 Function 和 String。固然這並非錯,從工具的角度來說咱們應用函數應該遵照函數創造者設定的規則,超出規則後出現錯誤並非說做者的函數必定有問題,也多是咱們太過於調皮了(好比番茄西紅柿須要用平底鍋來炒,但廚師非要用電飯煲,這是廚師的錯仍是平底鍋生產商的錯 ─=≡Σ((( つ•̀ω•́)つ)。
言歸正傳當傳入合理的 iteratee 值時,其實整個函數的重點仍是 group
函數內部的 cb
函數,由於咱們能夠看源碼 _.groupBy
上的回調最終是落實到 cb
上,將一個函數比做一個公共房間,衆多人就是傳入傳出的參數,那麼 cb 就是門禁卡識別每一個人的身份併發身份牌。若是 iteratee 是 String 則用 _.property
處理恰到好處(生成獲取屬性值的函數),若是是 Function 也只是在 if (_.has(result, key)) result[key].push(value); else result[key] = [value];
以前經過回調生成相應的 key 值。
_.indexBy = group(function(result, value, key) { result[key] = value; });
官網釋義 給定一個list,和 一個用來返回一個在列表中的每一個元素鍵 的iterator 函數(或屬性名),返回一個每一項索引的對象。關鍵代碼參考 _.groupBy
,兩者的二區別也之有一行代碼,理解起來並不難,我就再也不水文字了。
_.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; });
官網釋義 排序一個列表組成一個組,而且返回各組中的對象的數量的計數。相似groupBy,可是不是返回列表的值,而是返回在該組中值的數目。其實就是對匹配成功的元素計數。
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
reStrSymbol 用於正則函數,這一塊我也不是很熟悉,可是我找到了兩篇文章作了參考,Unicode Regular Expressions, Surrogate Points and UTF-8、
Re: Java char and Unicode 3.0+ (was:Canonical equivalence in rendering: mandatory or recommended?)、unicode。另外知乎上也有人對這句話作了判斷:
[^\ud800-\udfff] 普通的 BMP 字符,表示不包含代理對代碼點的全部字符 [\ud800-\udbff][\udc00-\udfff] 成對的代理項對,表示合法的代理對的全部字符 [\ud800-\udfff] 未成對的代理項字,表示代理對的代碼點(自己不是合法的Unicode字符)
以上僅供參考,我也不是很清楚,等我作好這方面功課的時候再從新說這個話題。
_.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (_.isString(obj)) { return obj.match(reStrSymbol); } if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj); };
官網說 把list(任何能夠迭代的對象)轉換成一個數組,在轉換 arguments 對象時很是有用,並給出一個 (function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
,說內心話每當看到 arguments 的時候我第一個印象是 Array.prototype.slice.call(arguments, indexes);
,這裏做者對待 Array 的原理一樣是這個。_.toArray
函數自己沒有重點,無非就是根據字符串、數組、對象進行數組轉換,須要注意的是當轉換 Object 的時候會忽略 key-value 的 key,只單獨把 value 放到數組中,另外就是 if (_.isArray(obj))
和 if (isArrayLike(obj))
,顧名思義第一個是判斷數組,第二個難道是考慮到 {'length':[1,2,3,4]}
這種數據結構的狀況?
_.size = function(obj) { if (obj == null) return 0; return isArrayLike(obj) ? obj.length : _.keys(obj).length; };
_.size
用於返回傳入參數的長度,包括但不限於 Object、Array 、 String 和 Function,Function 返回的是 Function 中傳入參數的個數(arguments)。另外 Map 這裏有個坑,Map返回值是12,衆所周知 Map是一個大的對象,因此返回值是它的12個基本屬性的個數。
_.partition = group(function(result, value, pass) { result[pass ? 0 : 1].push(value); }, true);
_.partition
是第四個用 group 函數包裝的函數,用來對傳入 obj 作判斷時返回符合回調斷言的結果集以及不符合的結果集,從 result[pass ? 0 : 1].push(value)
這裏就可見一斑了,也就是說 group 的第三個傳參 partition 也就是爲了 _.partition
而存在。partition 使 result 的設定爲固定的 [[][]]
,這種寫法我以爲並非看上去最優雅地,理想狀況是最好不存在第三個參數纔對,但這必定是相對節約性能的,面對可節約的性能怎麼取捨已經很清楚了。