摘要:在中,真值檢測函數的參數被命名為,有斷言的意思,非常形象。函數的功能是檢測一個對象或數組是否包含指定的某個元素。
順著underscore源碼的順序讀下來,弄懂了之前underscore的基本結構,接下來看看underscore為我們提供的一些關于集合的API。
迭代關于迭代,我們都知道ES5原生方法也提供了迭代函數供我們使用,而在underscore中的迭代則是對原生的迭代函數進行了封裝優化升級。在underscore中,迭代的對象不僅僅是數組對象,還支持Array,Object的迭代,對Object的迭代的依據是對象的鍵值對(key-value),看看 underscore中_.each是如何實現的:
/** * each方法將ES5的forEach換為了函數式表達 * @param obj 待迭代集合 * @param iteratee 迭代過程中每個被迭代元素的回調函數 * @param context 上下文 * @example * // 數組迭代 * _.each([1, 2, 3], alert); * // 對象迭代 * _.each({one: 1, two: 2, three: 3}, alert); */ _.each = _.forEach = function (obj, iteratee, context) { //優化回調 iteratee = optimizeCb(iteratee, context); var i, length; // 判斷是數組還是對象 if (isArrayLike(obj)) { for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj) } } else { var keys = _.keys(obj) for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj) } } // 返回對象自身 以便于鏈式調用 return obj };
看以上源碼可知,_.each傳入三個參數,主要的是第二個iteratee回調函數,然后再通過optimizeCb優化回調,返回對應的回調(optimizeCb可以查看第一部分)。
array迭代的是數組的每個元素,傳入的三個參數分別為數組的值,對應值的下標,數組本身。Object迭代的元素是對象的每個鍵值對key-value,傳入的參數為對象的key所對應的值,對象的key值,對象本身。
ES5原生方法也提供map和reduce方法,它們提供了一種對列表操作的思路,是函數式編程重要組成部分。具體map和reduce可以去MDN上查看相關API。
map在underscore中的實現它的實現思路是:
返回一個新的列表或元素
對列表中的值進行遍歷,用指定函數func作用于每個遍歷的元素,輸出一個新的值放到新的列表中
_.map = _.collect = function(obj, iteratee, context) { iteratee = cb(iteratee, context) //考慮數組和對象 var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length) // 初始化定長數組 for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index results[index] = iteratee(obj[currentKey], currentKey, obj) } return results }
使用用例:
對數組使用
var res = _.map([1,2,3], function(elem, index, array) { return elem * 2 }) // => [2,4,6]
對對象使用
var obj = { name: "lzb", age: "20", sex: "male" } var res = _.map(obj, function(value, key, obj) { return key }) // => name age sexreduce在underscore中的實現
reduce相對于map的實現復雜了一些,underscore首先在外部實現了reduce函數的工廠函數createReduce,這個函數實現了以下功能:
區分reduce的開始方向(參數dir),是從首端開始末端開始
memo記錄最新的結果
reduce的執行過程大概是:
設置一個memo變量緩存當前規約過程的結果
如果用戶為初始化memo,則memo的值為序列的第一個值
遍歷當前集合,對當前遍歷到的元素按傳入的func進行規約操作,刷新memo
遍歷結束,返回memo
createReduce的實現:
/** * reduce函數的工廠函數, 用于生成一個reducer, 通過參數決定reduce的方向 * @param dir 方向 left or right * @returns {function} */ function createReduce(dir) { function iterator(obj, iteratee, memo, keys, index, length) { for (; idnex > 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index // memo 用來記錄最新的 reduce 結果 // 執行 reduce 回調, 刷新當前值 memo = iteratee(memo, obj[currentKey], currentKey, obj) } } /** * @param obj 傳入的對象 * @param iteratee 回調函數 * @param memo 初始化累加器的值 * @param context 執行上下文 */ return function (obj, iteratee, memo, context) { iteratee = optimizeCb(iteratee, context, 4) var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length index = dir > 0 ? 0 : length - 1 // 如果沒有傳入memo初始值 則從左第一個為初始值 從右則最后一個為初始值 if (arguments.length < 3) { memo = obj[keys ? keys[index] : index] index += dir } return iterator(obj, iteratee, memo, keys, index, length) } }
最后,underscore暴露了兩個供使用的方法
// 由左至右進行規約 _.reduce = _.foldl = _.inject = createReduce(1); // 由右至左進行規約 _.reduceRight = _.foldr = createReduce(-1);
使用用例:
對數組使用
var sum = _.reduce([1,2,3,4], function(prev, current, index, arr) { return prev + current }, 0) // => 10
對對象使用
var scores = { english: 93, math: 88, chinese: 100 }; var total = _.reduce(scores, function(prev, value, key, obj){ return prev+value; }, 0); // => total: 281真值檢測函數
在underscore中,除了提供_.each,_.map._.reduce等函數操作集合,還提供了_.filter, _.reject, _.every, _.some這幾個基于邏輯判斷的集合操作函數。這些API都依賴于用戶提供的真值檢測函數來返回對應的結果。
在underscore中,真值檢測函數的參數被命名為predicate,predicate有斷言的意思,非常形象。當然,predicate依舊會通過cb優化。
看看_.filter的實現
/** * 根據真值檢測函數 過濾對象 * 檢測通過符合條件 保留元素 * @param obj * @param predicate * @param context * @example * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); * => [2, 4, 6] */ _.filter = _.select = function (obj, predicate, context) { var results = [] // 優化回調 predicate = cb(predicate, context) _.each(obj, function (value, index, list) { if (predicate(value, index, list)) results.push(value) }) return results }
根據傳入的元素信息,檢測并返回對應boolean值,決定當前元素要被保留。
_.reject上面的_.filter函數是元素符合檢測條件就保留,而_.reject函數則是與_.filter相反的功能
我們來看看underscore中_.reject的實現
/** * filter的反運算, * 如果真值檢測通過, 元素被丟棄 */ _.reject = function (obj, predicate, context) { return _.filter(obj, _negate(cb(predicate)), context) }
可以看到,這個函數只有一行代碼,非常簡短。那么,這其中的_.negate函數又是什么呢?猜測下,negate在英語中有否定的意思,那么跟_.reject的功能就有了一定的聯系, 下面看看_.negate的實現
_.negate = function(predicate) { return function() { return !predicate.apply(this, arguments) } }
可以看到,_.negate得到了反義predicate的執行結果,減少了大量重復的代碼,值得學習。
_.every迭代對象里的每個元素,只有每個元素都通過真值檢測函數,才返回true。
/** * @param obj * @param predicate * @param context * @example * _.every([true, 1, null, "yes"], _.identity); * => false */ _.every = _.all = function (obj, predicate, context) { predicate = cb(predicate, context) var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index if (!predicate(obj[currentKey], currentKey, obj)) return false } return true }_.some
這個API跟_.every差不多,從英語單詞的含義我們也可以猜出它的功能,即迭代對象的所有元素,如果有任意一個通過真值檢測,則返回true。
/** * @param obj * @param predicate * @param context * @example * _.some([null, 0, "yes", false]); * => true */ _.some = _.any = function (obj, predicate, context) { predicate = cb(predicate, context) var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index if (predicate(obj[currentKey], currentKey, obj)) return true } return false }_.contains
_.contains函數的功能是檢測一個對象或數組是否包含指定的某個元素。
/** * @param obj 待檢測對象 * @param item 指定的元素 * @param fromIndex 從哪個位置開始找 * @param guard * @example * _.contains([1,2,3], 3) * => true */ _.contains = _.includes = _.include = function (obj, item, fromIndex, guard) { if (!isArrayLike(obj)) obj = _.values(obj) if (typeof fromIndex != "number" || guard) fromIndex = 0 return _.indexOf(obj, item.fromIndex) >= 0 }
從代碼上看,還是比較容易理解的,這里主要用到了underscore內部提供的兩個函數,_.values和_.indexOf,從名字上我們也可以猜出它們之間的功能,如果傳入的對象,則取出該對象所有的值,然后再進行查找比較,看看_values的實現:
/** * 獲得一個對象的所有value * @param obj 對象 * @returns {Array} 值序列 * @example * _.values({one: 1, two: 2, three: 3}); * // => [1, 2, 3] */ _.values = function (obj) { var keys = _.keys(obj) var length = keys.length var values = Array(length) for (var i = 0; i < length; i++) { values[i] = obj[keys[i]] } return values }
而_,indexOf的實現就比較復雜了,這是underscore中提供的關于查找的API,詳細介紹將在下一篇總結寫出。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88627.html
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:所以,剛開始,我從源碼比較短的包含注釋只有行開始學習起。一般,在客戶端瀏覽器環境中,即為,暴露在全局中。學習以后判斷直接使用看起來也優雅一點滑稽臉。在的函數視線中,的作用執行一個傳入函數次,并返回由每次執行結果組成的數組。 前言 最近在社區瀏覽文章的時候,看到了一位大四學長在尋求前端工作中的面經,看完不得不佩服,掌握知識點真是全面,無論是前端后臺還是其他,都有涉獵。 在他寫的文章中,有...
摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個系列后,再跟著其他系列的文章接著學習。如何閱讀我在寫系列的時候,被問的最多的問題就是該怎么閱讀源碼我想簡單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個系列再見啦 前言 別名:《underscore 系列 8 篇正式完結!》 介紹 underscore 系列是我寫的第三個系列,前兩個系列分別是 JavaScript 深入系列、...
摘要:本文同步自我得博客我在這個系列的第一篇文章說過,我學是為了在學的時候少一些阻礙,從第一篇的寫作時間到今天,大概也有個十幾二十天,感覺拖得有點久,所以今天將會是源碼解析系列的最后一篇文章,我會在這篇文章中介紹剩下的所有函數。 本文同步自我得博客:http://www.joeray61.com 我在這個系列的第一篇文章說過,我學underscore是為了在學backbone的時候少一些阻礙...
摘要:本文同步自我得博客最近準備折騰一下,在事先了解了之后,我知道了對這個庫有著強依賴,正好之前也沒使用過,于是我就想先把徹底了解一下,這樣之后折騰的時候也少一點阻礙。 本文同步自我得博客:http://www.joeray61.com 最近準備折騰一下backbone.js,在事先了解了backbone之后,我知道了backbone對underscore這個庫有著強依賴,正好undersc...
閱讀 1905·2021-11-24 11:16
閱讀 3260·2021-09-10 10:51
閱讀 3200·2021-08-03 14:03
閱讀 1266·2019-08-29 17:03
閱讀 3245·2019-08-29 12:36
閱讀 2232·2019-08-26 14:06
閱讀 497·2019-08-23 16:32
閱讀 2678·2019-08-23 13:42