underscore.js源碼解析(四)

来源:http://www.cnblogs.com/WhiteBlade/archive/2016/03/20/5297199.html
-Advertisement-
Play Games

沒看過前幾篇的可以猛戳這裡: underscore.js源碼解析(一) underscore.js源碼解析(二) underscore.js源碼解析(三) 本文解析的underscore.js版本是1.8.3 _.pluck的作用就是獲取數據對象中的相應屬性,然後存在數組當中返回 _.where就是


沒看過前幾篇的可以猛戳這裡:

underscore.js源碼解析(一)

underscore.js源碼解析(二)

underscore.js源碼解析(三)

underscore.js源碼GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js

本文解析的underscore.js版本是1.8.3

 _.pluck

1   _.pluck = function(obj, key) {
2     return _.map(obj, _.property(key));
3   };

 _.pluck的作用就是獲取數據對象中的相應屬性,然後存在數組當中返回

_.where

1   _.where = function(obj, attrs) {
2     return _.filter(obj, _.matcher(attrs));
3   };

_.where就是遍曆數據,然後返回所有滿足條件的鍵值對,存在數組當中

_.findWhere

1   _.findWhere = function(obj, attrs) {
2     return _.find(obj, _.matcher(attrs));
3   }

_.findWhere和where不同的是只返回第一個滿足條件的

_.max

 1   _.max = function(obj, iteratee, context) {
 2     var result = -Infinity, lastComputed = -Infinity,
 3         value, computed;
 4     //如果沒有iteratee
 5     if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
 6       obj = isArrayLike(obj) ? obj : _.values(obj);
 7       //迴圈遍歷求出最大值
 8       for (var i = 0, length = obj.length; i < length; i++) {
 9         value = obj[i];
10         if (value != null && value > result) {
11           result = value;
12         }
13       }
14     } else {
15     //有iteratte的情況
16       iteratee = cb(iteratee, context);
17       _.each(obj, function(v, index, list) {
18         //迭代出的最大值結果
19         computed = iteratee(v, index, list);
20         if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
21           result = v;
22           lastComputed = computed;
23         }
24       });
25     }
26     return result;
27   };

獲取obj中的最大值

_.min

 1   _.min = function(obj, iteratee, context) {
 2     var result = Infinity, lastComputed = Infinity,
 3         value, computed;
 4     if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
 5       obj = isArrayLike(obj) ? obj : _.values(obj);
 6       for (var i = 0, length = obj.length; i < length; i++) {
 7         value = obj[i];
 8         if (value != null && value < result) {
 9           result = value;
10         }
11       }
12     } else {
13       iteratee = cb(iteratee, context);
14       _.each(obj, function(v, index, list) {
15         computed = iteratee(v, index, list);
16         if (computed < lastComputed || computed === Infinity && result === Infinity) {
17           result = v;
18           lastComputed = computed;
19         }
20       });
21     }
22     return result;
23   };
View Code

獲取最小值,跟max差不多,就不分析了

_.sample

 1   _.sample = function(obj, n, guard) {
 2     //判斷是否有n這個參數,如果沒有
 3     if (n == null || guard) {
 4       if (!isArrayLike(obj)) obj = _.values(obj);
 5       //隨機取一個隨機樣本
 6       return obj[_.random(obj.length - 1)];
 7     }
 8     var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
 9     var length = getLength(sample);
10     n = Math.max(Math.min(n, length), 0);
11     var last = length - 1;
12     //下麵就是一個打亂數組順序的過程
13     for (var index = 0; index < n; index++) {
14       //生成一個隨機的索引
15       var rand = _.random(index, last);
16       var temp = sample[index];
17       sample[index] = sample[rand];
18       sample[rand] = temp;
19     }
20     //最後返回前n個
21     return sample.slice(0, n);
22   };

 從數組中取出隨意樣本,如果有n則從被打亂的數組中返回前n個數據,如果沒有則返回一個隨機樣本

_.shuffle

1   _.shuffle = function(obj) {
2     return _.sample(obj, Infinity);
3   };

 _.shuffle調用_.sample函數,並且出入infinity參數,來實現返回一個亂序的數組

_.sortBy

 1   _.sortBy = function(obj, iteratee, context) {
 2     var index = 0;
 3     iteratee = cb(iteratee, context);
 4     return _.pluck(_.map(obj, function(value, key, list) {
 5       return {
 6         value: value,
 7         index: index++,
 8         criteria: iteratee(value, key, list)
 9       };
10     }).sort(function(left, right) {
11      //先用criteria比較
12       var a = left.criteria;
13       var b = right.criteria;
14       if (a !== b) {
15         if (a > b || a === void 0) return 1;
16         if (a < b || b === void 0) return -1;
17       }
18       //如果相等再用index來排序
19       return left.index - right.index;
20     }), 'value');
21   };

 _.sortBy 就是一個返回按需排序後的數組,這個函數結構看上去挺複雜,其實拆分來看,就是調用_.pluck獲取對象的屬性值,然後裡面是_.map.sort()來做一個排序,最後返回一個數組,這樣拆分完是不是清晰了不少。

不理解sort的可以點擊這裡:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

 group

 1   var group = function(behavior, partition) {
 2     return function(obj, iteratee, context) {
 3       //判斷是否需要拆分為兩個數組,這個在下麵介紹_.partition的時候具體解釋
 4       var result = partition ? [[], []] : {};
 5       iteratee = cb(iteratee, context);
 6       _.each(obj, function(value, index) {
 7         var key = iteratee(value, index, obj);
 8         behavior(result, value, key);
 9       });
10       return result;
11     };
12   };

 group第一眼一看也是有點複雜,函數裡面返回函數,然後behavior參數還是個函數

結構簡化之後其實就是下麵這樣:
function A(B){
     return function(){
         B(); 
     }
}

從return的函數裡面來看就是遍歷obj取出相應的key,然後把它傳給behavior中,具體behavior這裡怎麼理解,放到下麵groupBy一起說,容易理解一些。

_.groupBy

1   _.groupBy = group(function(result, value, key) {
2     //判斷result中是否有key值,如果有則加入value,如果沒有將把value放入數組當中
3     if (_.has(result, key)) result[key].push(value); else result[key] = [value];
4   });
_.groupBy的作用就是根據相應的參數將集合進行分組 _.groupBy就是調用了group函數,把group融合到其中更容易理解一些
1 _.groupBy = function(obj, iteratee, context) {
2       var result = partition ? [[], []] : {};
3       iteratee = cb(iteratee, context);
4       _.each(obj, function(value, index) {
5         var key = iteratee(value, index, obj);
6         if (_.has(result, key)) result[key].push(value); else result[key] = [value];
7       });
8       return result;
9  };

 這樣看group的結構徹底清晰了。

_.indexBy

1   _.indexBy = group(function(result, value, key) {
2     result[key] = value;
3   });

返回每一項索引的對象

_.countBy

1   _.countBy = group(function(result, value, key) {
2     if (_.has(result, key)) result[key]++; else result[key] = 1;
3   });

 這個就是返回符合條件的對象數量

 

  _.partition = group(function(result, value, pass) {     result[pass ? 0 : 1].push(value);   }, true);

這就是一個按要求把一個數組拆分成兩個的函數,調用group函數,遍曆數組,將符合條件的放在第一個數組當中,不符合的放在第二個函數當中

_.toArray

 1   _.toArray = function(obj) {
 2     //如果為空,返回空數組
 3     if (!obj) return [];
 4     //如果是數組,拷貝數組
 5     if (_.isArray(obj)) return slice.call(obj);
 6     if (_.isString(obj)) {
 7       // 如果是字元串,則利用正則表達式提取匹配項
 8       return obj.match(reStrSymbol);
 9     }
10     //如果類數組,通過map方法轉化數組
11     if (isArrayLike(obj)) return _.map(obj);
12     //對象,取出屬性值存入數組
13     return _.values(obj);
14   };

_.size

1   _.size = function(obj) {
2     //判斷是否為空
3     if (obj == null) return 0;
4     //如果是數組則返回數組長度,如果是對象返回屬性的數量
5     return isArrayLike(obj) ? obj.length : _.keys(obj).length;
6   };

_.initial

1   _.initial = function(array, n, guard) {
2     return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
3   };

返回篩選後的數組,n是第幾個開始,如果n不存在那麼就是array.length-1,也就是數組全拷貝一遍,Math.max是為了規避負數的情況

_.first

1   _.first = _.head = _.take = function(array, n, guard) {
2     //參數判空處理
3     if (array == null) return void 0;
4     if (n == null || guard) return array[0];
5     return _.initial(array, array.length - n);
6   };

first就是取前幾個元素的作用,調用了initial函數

_.rest

1   _.rest = _.tail = _.drop = function(array, n, guard) {
2     return slice.call(array, n == null || guard ? 1 : n);
3   };

_.rest是獲取第n個元素之後的元素

_.last

1   _.last = function(array, n, guard) {
2     if (array == null) return void 0;
3     if (n == null || guard) return array[array.length - 1];
4     return _.rest(array, Math.max(0, array.length - n));
5   };

返回最後n個元素,結構跟first差不多

flatten

 1   var flatten = function(input, shallow, strict, output) {
 2      //output是存放結果的數組
 3     output = output || [];
 4     var idx = output.length;
 5     //遍歷input,input是要處理的數組
 6     for (var i = 0, length = getLength(input); i < length; i++) {
 7       var value = input[i];
 8       //判斷是不是數組或者arguments對象
 9       if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
10         // 是否只合併一次
11         if (shallow) {
12           var j = 0, len = value.length;
13           while (j < len) output[idx++] = value[j++];
14         } else {
15           //如果不是,則遞歸調用flatten
16           flatten(value, shallow, strict, output);
17           idx = output.length;
18         }
19       //如果不是數組,並且是非嚴格的
20       } else if (!strict) {
21         //直接將value拷貝到output數組當中
22         output[idx++] = value;
23       }
24     }
25     return output;
26   };

flatten作用就是將多維數組合併成一維數組,如果參數shallow為true的話,就只合併一次

_.difference

1   _.difference = restArgs(function(array, rest) {
2     rest = flatten(rest, true, true);
3     return _.filter(array, function(value){
4       return !_.contains(rest, value);
5     });
6   });

作用是剔除第一數組中其他數組也有的元素,它是先調用flatten將其合併,再調用filter選出符合條件的,裡面的條件是!_.cotains,也就是不存在的,所以最後取出的就是剔除完之後的元素數組了

_.uniq

 1   _.uniq = _.unique = function(array, isSorted, iteratee, context) {
 2     //如果沒有isSorted參數則對參數進行調整
 3     if (!_.isBoolean(isSorted)) {
 4       context = iteratee;
 5       iteratee = isSorted;
 6       isSorted = false;
 7     }
 8     //參數不為空,生成回調函數
 9     if (iteratee != null) iteratee = cb(iteratee, context);
10     var result = [];
11     var seen = []; //緩存數據用的
12     for (var i = 0, length = getLength(array); i < length; i++) {
13       var value = array[i],
14             computed = iteratee ? iteratee(value, i, array) : value;
15       //如果是排序好的
16       if (isSorted) {
17         //因為是排序好的,按順序比較就好了
18         if (!i || seen !== computed) result.push(value);
19         seen = computed;
20       } else if (iteratee) {
21         //處理對象,computed是上面回調函數返回的結果,然後比較,沒有就添加
22         if (!_.contains(seen, computed)) {
23           seen.push(computed);
24           result.push(value);
25         }
26      //判斷result里是否有value,如果沒有就添加
27       } else if (!_.contains(result, value)) {
28         result.push(value);
29       }
30     }
31     return result;
32   };

就是一個去重的函數,分別對有序的,對象和普通三種情況分別進行處理

_.intersection

 1   _.intersection = function(array) {
 2     var result = [];
 3     var argsLength = arguments.length;
 4     //遍歷第一個數組
 5     for (var i = 0, length = getLength(array); i < length; i++) {
 6       var item = array[i];
 7       //判斷result中是否有
 8       if (_.contains(result, item)) continue;
 9       var j;
10       //遍歷其他數組
11       for (j = 1; j < argsLength; j++) {
12         //如果其他數組中,存在沒有的情況就結束迴圈
13         if (!_.contains(arguments[j], item)) break;
14       }
15       //如果迴圈完,說明每個裡面都有,那麼就是我們想要的交集結果
16       if (j === argsLength) result.push(item);
17     }
18     return result;
19   };

 作用就是去數組的交集,就是拿第一個數組中每一個元素跟後面的數組作比較

_.unzip

 1   _.unzip = function(array) {
 2     //取出一個最大長度值
 3     var length = array && _.max(array, getLength).length || 0;
 4     var result = Array(length);
 5     //迴圈取出每個數組中相同位置的元素,最後組成一個新的二維數組
 6     for (var index = 0; index < length; index++) {
 7       result[index] = _.pluck(array, index);
 8     }
 9     return result;
10   };

_.object

 1   _.object = function(list, values) {
 2     var result = {};
 3     for (var i = 0, length = getLength(list); i < length; i++) {
 4       if (values) {
 5         result[list[i]] = values[i];
 6       } else {
 7         result[list[i][0]] = list[i][1];
 8       }
 9     }
10     return result;
11   };
View Code

 作用就是將數組轉化為對象

_.range

 1   _.range = function(start, stop, step) {
 2     //判斷是否有stop參數
 3     if (stop == null) {
 4       stop = start || 0;
 5       start = 0;
 6     }
 7     //step參數不存在的時候賦予預設值
 8     if (!step) {
 9       step = stop < start ? -1 : 1;
10     }
11     //獲取數組長度
12     var length = Math.max(Math.ceil((stop - start) / step), 0);
13     var range = Array(length);
14     for (var idx = 0; idx < length; idx++, start += step) {
15       //數組賦值
16       range[idx] = start;
17     }
18     return range;
19   };

作用是按需生成一個整數數組

_.chunk

 1   _.chunk = function(array, count) {
 2     //如果count為空或小於1,則返回空
 3     if (count == null || count < 1) return [];
 4     var result = [];
 5     var i = 0, length = array.length;
 6     while (i < length) {
 7       //進行拆分
 8       result.push(slice.call(array, i, i += count));
 9     }
10     return result;
11   };

小結

本打算上周就發出來的,但是忙著其他的事情耽擱了,underscore.js剩下的所有內容會在下一篇中都寫完(不出意外的話是下周發出來)。

感謝大家的觀看,也希望能夠和大家互相交流學習,有什麼分析的不對的地方歡迎大家批評指出

參考資料

http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370
 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文參考鏈接: http://baike.baidu.com/link?url=svs6WnjQKV7Ugx3SZke6BvyVy99OOE8I-zn8gSw0HFb-YD-IIcdf2F2h5WGslM4Q4Dog28oXyjX51lnvF2n0Kq
  • 利用SocketServer模塊來實現網路客戶端與伺服器併發連接非阻塞通信。首先,先瞭解下SocketServer模塊中可供使用的類:BaseServer:包含伺服器的核心功能與混合(mix-in)類掛鉤;這個類只用於派生,所以不會生成這個類的實例;可以考慮使用TCPServer和UDPServer
  • Warning messages: 1: In odbcDriverConnect("DSN=Rdata;UID=root") : [RODBC] ERROR: state IM002, code 0, message [Microsoft][ODBC 驅動程式管理器] 未發現數據源名稱並且未指定默
  • 在任何模式的編程過程中都無法避免副作用的產生。我們可以用F[A]這種類型模擬FP的運算指令:A是可能產生副作用的運算,F[_]是個代數數據類型ADT(Algebraic Data Type),可以實現函數組合(functional composition),我們可以不用理會A,先用F[_]來組合形成
  • 這段時間一直在學習flask框架,看到flask擴展中有一個mail插件,所以今天就給大家演示如果發郵件。 首先我註冊了一個163郵箱,需要開啟smtp功能,因為咱們python發送郵件經過的是smtp.163.com(網易的電子郵件伺服器)。 註冊好163郵箱,然後開啟smtp功能,如下圖所示:
  • 大家好,我是小Alan,很高興大家能夠看到這篇小小的技術點文章,這還是從參加工作以來,小Alan寫的第一篇博文。喜歡能夠給一些朋友帶來方便。 說到eclipse編碼格式的設置其實一個非常非常小的事情,但是在eclipse的開發使用中卻又是一個無法忽視的問題,它甚至會影響到我們的工作以及和團隊成員之間
  • 學習head first python一書的資料,Android、gae環境程式,sl4a_r3.apk,GoogleAppEngine-1.5.2.msi,GoogleAppEngine-1.5.2.msi等
  • 通過分析如下代碼,大致瞭解Disruptor的原理 1. 第2行代碼 EventFactory<LongEvent> eventFactory = new LongEventFactory(); 數據工廠類構造單個數據,disruptor使用此工廠類預分配數據。 2. 第5行代碼 final Dis
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...