本文同步自我得博客:http://www.joeray61.comnode
最近十幾天都在忙畢業論文的事,因此上一次爲你們介紹完underscore這個框架的結構(或者說是這個框架的設計思路)以後就一直沒動靜了,今天我又滿血復活了,讓咱們繼續來探索underscore的源碼奧祕吧。
沒看過上一篇文章的朋友能夠戳這裏:underscore源碼解析(一)
今天的內容是underscore裏面封裝的一些函數,我將逐個介紹,我們直接入正題吧segmentfault
var each = _.each = _.forEach = function(obj, iterator, context) { // 不處理空值 if(obj == null) return; if(nativeForEach && obj.forEach === nativeForEach) { // 若是宿主環境支持, 則優先調用JavaScript 1.6提供的forEach方法 obj.forEach(iterator, context); } else if(obj.length === +obj.length) { // 對[數組]中每個元素執行處理器方法 for(var i = 0, l = obj.length; i < l; i++) { if( i in obj && iterator.call(context, obj[i], i, obj) === breaker) return; } } else { // 對{對象}中每個元素執行處理器方法 for(var key in obj) { if(_.has(obj, key)) { if(iterator.call(context, obj[key], key, obj) === breaker) return; } } } };
這個函數的實現思想其實很簡單,若是宿主環境(通常爲瀏覽器或者node.js)支持原生的forEach方法,就調用原生的,不然就遍歷該數組或者對象,依次調用處理器方法
值得一提的是在判斷是不是數組的時候,這裏的代碼爲數組
obj.length === +obj.length瀏覽器
這實際上是一種鴨式辨型的斷定方法,具體能夠參見我在SF上提過的一個問題:點我框架
_.map = _.collect = function(obj, iterator, context) { // 用於存放返回值的數組 var results = []; if(obj == null) return results; // 優先調用宿主環境提供的map方法 if(nativeMap && obj.map === nativeMap) return obj.map(iterator, context); // 迭代處理集合中的元素 each(obj, function(value, index, list) { // 將每次迭代處理的返回值存儲到results數組 results[results.length] = iterator.call(context, value, index, list); }); // 返回處理結果 if(obj.length === +obj.length) results.length = obj.length; return results; };
map/collect函數與each的區別在於map/collect會存儲每次迭代的返回值, 並做爲一個新的數組返回ide
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { // 經過參數數量檢查是否存在初始值 var initial = arguments.length > 2; if(obj == null) obj = []; // 優先調用宿主環境提供的reduce方法 if(nativeReduce && obj.reduce === nativeReduce && false) { if(context) iterator = _.bind(iterator, context); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); } // 迭代處理集合中的元素 each(obj, function(value, index, list) { if(!initial) { // 若是沒有初始值, 則將第一個元素做爲初始值; 若是被處理的是對象集合, 則默認值爲第一個屬性的值 memo = value; initial = true; } else { // 記錄處理結果, 並將結果傳遞給下一次迭代 memo = iterator.call(context, memo, value, index, list); } }); if(!initial) throw new TypeError('Reduce of empty array with no initial value'); return memo; };
這個函數的做用是將集合中每一個元素放入迭代處理器, 並將本次迭代的返回值做爲memo傳遞到下一次迭代, 通常用於累計結果或鏈接數據函數
_.reduceRight = _.foldr = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if(obj == null) obj = []; // 優先調用宿主環境提供的reduceRight方法 if(nativeReduceRight && obj.reduceRight === nativeReduceRight) { if(context) iterator = _.bind(iterator, context); return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); } // 逆轉集合中的元素順序 var reversed = _.toArray(obj).reverse(); if(context && !initial) iterator = _.bind(iterator, context); // 經過reduce方法處理數據 return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator); };
這個函數與reduce類似,不過它是逆向迭代集合中的元素prototype
_.find = _.detect = function(obj, iterator, context) { // result存放第一個可以經過驗證的元素 var result; // 經過any方法遍歷數據, 並記錄經過驗證的元素 any(obj, function(value, index, list) { // 若是處理器返回的結果被轉換爲Boolean類型後值爲true, 則記錄當前值並返回當前元素 if(iterator.call(context, value, index, list)) { result = value; return true; } }); return result; };
這個方法的做用是遍歷集合中的元素, 返回可以經過處理器驗證的第一個元素設計
_.filter = _.select = function(obj, iterator, context) { // 用於存儲經過驗證的元素數組 var results = []; if(obj == null) return results; // 優先調用宿主環境提供的filter方法 if(nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); // 迭代集合中的元素, 並將經過處理器驗證的元素放到數組中並返回 each(obj, function(value, index, list) { if(iterator.call(context, value, index, list)) results[results.length] = value; }); return results; };
這個方法與find做用相似, 但它會記錄下集合中全部經過驗證的元素3d
_.reject = function(obj, iterator, context) { var results = []; if(obj == null) return results; each(obj, function(value, index, list) { if(!iterator.call(context, value, index, list)) results[results.length] = value; }); return results; };
這個方法的代碼裏面我沒有加註釋,由於整個代碼與filter/select方法幾乎同樣,不一樣點在於向results數組裏添加元素的時候判斷條件是相反的,也就是說這個方法的做用是返回沒有經過處理器驗證的元素列表
_.every = _.all = function(obj, iterator, context) { var result = true; if(obj == null) return result; // 優先調用宿主環境提供的every方法 if(nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); // 迭代集合中的元素 each(obj, function(value, index, list) { // 這裏我不太理解,爲何藥寫成 result = (result && iterator.call(context, value, index, list)) 而不是 result = iterator.call(context, value, index, list) if(!( result = result && iterator.call(context, value, index, list))) return breaker; }); return !!result; };
這個方法的做用是若是集合中全部元素均能經過處理器驗證, 則返回true
var any = _.some = _.any = function(obj, iterator, context) { // 若是沒有指定處理器參數, 則使用默認的處理器函數,該函數會返回參數自己 iterator || ( iterator = _.identity); var result = false; if(obj == null) return result; // 優先調用宿主環境提供的some方法 if(nativeSome && obj.some === nativeSome) return obj.some(iterator, context); // 迭代集合中的元素 each(obj, function(value, index, list) { if(result || ( result = iterator.call(context, value, index, list))) return breaker; }); return !!result; };
該函數的做用是檢查集合中是否有任何一個元素在被轉換成Boolean類型時是否爲true
_.include = _.contains = function(obj, target) { var found = false; if(obj == null) return found; // 優先調用宿主環境提供的Array.prototype.indexOf方法 if(nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; // 經過any方法迭代集合中的元素, 驗證元素的值和類型與目標是否徹底匹配 found = any(obj, function(value) { return value === target; }); return found; };
這個函數用於檢查集合中是否有值與目標參數徹底匹配,包括數據類型
今天先介紹以上10個函數的實現細節,以後還會繼續帶來其餘函數的介紹,歡迎你們提出指正和建議,thx for reading, hope u enjoy