underscore.js源碼解析(三)

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

最近工作比較忙,做不到每周兩篇了,周末趕著寫吧,上篇我針對一些方法進行了分析,今天繼續。 沒看過前兩篇的可以猛戳這裡: underscore.js源碼解析(一) underscore.js源碼解析(二) underscore.js源碼GitHub地址: https://github.com/jash


最近工作比較忙,做不到每周兩篇了,周末趕著寫吧,上篇我針對一些方法進行了分析,今天繼續。

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

underscore.js源碼解析(一)

underscore.js源碼解析(二)

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

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

_.map/_.collect

 1   _.map = _.collect = function(obj, iteratee, context) {
 2     iteratee = cb(iteratee, context);
 3     //判斷是否是數組,是的話就將裡面的屬性取出
 4     var keys = !isArrayLike(obj) && _.keys(obj),
 5         length = (keys || obj).length,
 6         results = Array(length);
 7     for (var index = 0; index < length; index++) {
 8       var currentKey = keys ? keys[index] : index;
 9       results[index] = iteratee(obj[currentKey], currentKey, obj);
10     }
11     return results;
12   };

map和each的區別就是map是將最後的結果以數組的形式返回

createReduce

 1   var createReduce = function(dir) {
 2     var reducer = function(obj, iteratee, memo, initial) {
 3       //判斷是否是數組
 4       var keys = !isArrayLike(obj) && _.keys(obj),
 5           length = (keys || obj).length,
 6           //判斷迭代方向,dir為1是從左向右迭代,dir為-1是從右向左迭代(從_.reduce和_.reduceRight里就可以清晰的看出來)
 7           index = dir > 0 ? 0 : length - 1;
 8       //判斷是否存在初始值
 9       if (!initial) {
10         //如果沒有初始值,則將第一個設置為初始值,前面判斷了是否是數組,是數組返回數組否則返回對象
11         memo = obj[keys ? keys[index] : index];
12         //根據方向將初始位置賦給index,為了下邊遍歷使用
13         index += dir;
14       }
15       //進行遍歷
16       for (; index >= 0 && index < length; index += dir) {
17         var currentKey = keys ? keys[index] : index;
18         //進行迭代運算
19         memo = iteratee(memo, obj[currentKey], currentKey, obj);
20       }
21       return memo;
22     };
23 
24     return function(obj, iteratee, memo, context) {
25       //通過參數數量判斷是否有初始值
26       var initial = arguments.length >= 3;
27       //調用reducer()
28       return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
29     };
30   };

 

 看了上面的註釋你可以清晰的發現createReduce的結構已經很清晰了,內部調用reducer函數,先判斷有沒有初始值,如果沒有則根據迭代方向給初始值賦值,然後進行迴圈的迭代。

createPredicateIndexFinder

 1 var createPredicateIndexFinder = function(dir) {
 2     return function(array, predicate, context) {
 3       //迭代函數
 4       predicate = cb(predicate, context);
 5       var length = getLength(array);
 6       //判斷遍歷方向,dir為1是從左向右,dir為-1是從右向左
 7       var index = dir > 0 ? 0 : length - 1;
 8       for (; index >= 0 && index < length; index += dir) {
 9         //遍歷返回符合條件的是第幾個
10         if (predicate(array[index], index, array)) return index;
11       }
12       //否則返回-1
13       return -1;
14     };
15   };

 _.findIndex/_.findLastIndex

1   _.findIndex = createPredicateIndexFinder(1);
2   _.findLastIndex = createPredicateIndexFinder(-1);

調用上面的createPredicateIndexFinder內部函數,兩者只是遍歷的方向不同,最終返回相應的索引

_.findKey

 1   _.findKey = function(obj, predicate, context) {
 2     //迭代函數
 3     predicate = cb(predicate, context);
 4     var keys = _.keys(obj), key;
 5     for (var i = 0, length = keys.length; i < length; i++) {
 6       //獲取對象屬性
 7       key = keys[i];
 8       if (predicate(obj[key], key, obj)) return key;
 9     }
10   };

返回滿足條件的屬性

_.find / _.detect

 1   _.find = _.detect = function(obj, predicate, context) {
 2     var key;
 3     if (isArrayLike(obj)) {
 4       //如果是數組通過findIndex獲取滿足條件的索引
 5       key = _.findIndex(obj, predicate, context);
 6     } else {
 7       //如果是對象則通過findKey獲取滿足條件的屬性
 8       key = _.findKey(obj, predicate, context);
 9     }
10     //根據上面取到的索引或屬性,返回相應的值
11     if (key !== void 0 && key !== -1) return obj[key];
12   };

_.filter/_.select

1 _.filter = _.select = function(obj, predicate, context) {
2     var results = [];
3     predicate = cb(predicate, context);
4     //遍曆數據,將符合條件的存入results數組當中,並返回
5     _.each(obj, function(value, index, list) {
6       if (predicate(value, index, list)) results.push(value);
7     });
8     return results;
9   };

_.negate

1 _.negate = function(predicate) {
2     return function() {
3       return !predicate.apply(this, arguments);
4     };
5   };

_.negate就是一個取反的函數

_.reject

1 _.reject = function(obj, predicate, context) {
2     return _.filter(obj, _.negate(cb(predicate)), context);
3   };
_.reject調用了filter,只是做了一個判斷條件的取反操作,說明_.reject就是講不符合條件的存入數組並返回

_.every/_.all

 1 _.every = _.all = function(obj, predicate, context) {
 2     predicate = cb(predicate, context);
 3     //判斷數組還是對象
 4     var keys = !isArrayLike(obj) && _.keys(obj),
 5         length = (keys || obj).length;
 6     for (var index = 0; index < length; index++) {
 7       //數組獲取索引,對象獲取屬性
 8       var currentKey = keys ? keys[index] : index;
 9       //如果有不滿足條件的就返回false
10       if (!predicate(obj[currentKey], currentKey, obj)) return false;
11     }
12     return true;
13   };

_.every就是判斷是否所有的數據都滿足條件

_.some

 1 _.some = _.any = function(obj, predicate, context) {
 2     predicate = cb(predicate, context);
 3     var keys = !isArrayLike(obj) && _.keys(obj),
 4         length = (keys || obj).length;
 5     for (var index = 0; index < length; index++) {
 6       var currentKey = keys ? keys[index] : index;
 7       if (predicate(obj[currentKey], currentKey, obj)) return true;
 8     }
 9     return false;
10   };

跟every的判斷結構差不多,只不過最後的判斷條件不同,some是判斷時候有滿足條件的,有的話就返回true

_.values

1 _.values = function(obj) {
2     var keys = _.keys(obj);
3     var length = keys.length;
4     var values = Array(length);
5     for (var i = 0; i < length; i++) {
6       values[i] = obj[keys[i]];
7     }
8     return values;
9   };

_.values就是講obj的所有值拷貝到數組當中

_.sortedIndex

 1 _.sortedIndex = function(array, obj, iteratee, context) {
 2     iteratee = cb(iteratee, context, 1);
 3     var value = iteratee(obj);
 4     var low = 0, high = getLength(array);
 5     while (low < high) {
 6       var mid = Math.floor((low + high) / 2);
 7       if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
 8     }
 9     return low;
10   };

_.sortedIndex看過源碼就可以看出是二分法查找

createIndexFinder

 1   var createIndexFinder = function(dir, predicateFind, sortedIndex) {
 2     return function(array, item, idx) {
 3       var i = 0, length = getLength(array);
 4       if (typeof idx == 'number') {
 5         //判斷方向,1是從前到後,-1則為從後到前
 6         if (dir > 0) {
 7           i = idx >= 0 ? idx : Math.max(idx + length, i);
 8         } else {
 9           length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
10         }
11       //如果是排序好的就使用二分法
12       } else if (sortedIndex && idx && length) {
13         idx = sortedIndex(array, item);
14         //判斷找出的值是否一樣,是就返回這個值,否則返回-1
15         return array[idx] === item ? idx : -1;
16       }
17       if (item !== item) {
18         //對item為NaN的處理
19         idx = predicateFind(slice.call(array, i, length), _.isNaN);
20         return idx >= 0 ? idx + i : -1;
21       }
22       for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
23         //通過遍歷的方法找出item對應的索引
24         if (array[idx] === item) return idx;
25       }
26       //找不到則返回-1
27       return -1;
28     };
29   };

_.indexOf/_.lastIndexOf

1   _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
2   _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

_.contains/_.includes/_.include

1 //判斷是否包含對應的值
2 _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
3     //如果是對象,則將obj的所有值拷貝到數組當中
4     if (!isArrayLike(obj)) obj = _.values(obj);
5     if (typeof fromIndex != 'number' || guard) fromIndex = 0;
6     //查找是否存在這個值,如果存在,indexOf 返回相應的索引,則為true,如果不存在,indexOf 返回-1,則為false
7     return _.indexOf(obj, item, fromIndex) >= 0;
8   };

_.invoke

1 //對每一個元素都執行一次方法,最後把結果存入數組返回
2 _.invoke = restArgs(function(obj, method, args) {
3     var isFunc = _.isFunction(method);
4     return _.map(obj, function(value) {
5       //如果是函數則每個元素都執行一遍方法,如果不是,則返回所有的值,最後結果以數組的形式返回
6       var func = isFunc ? method : value[method];
7       return func == null ? func : func.apply(value, args);
8     });
9   });

小結

今天分析的幾個內部函數理解起來有點難度,理解要多看幾遍分析不同的情況,也許結合調用的例子理解起來會容易一點,分析我都寫在了代碼間的註釋里,這樣讀起源碼會容易一點,今天就寫到這裡吧,有點累了,明天還要上班,剩下的方法在之後的文章里都會分析到。

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

參考資料

https://segmentfault.com/a/1190000000531871
http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370
 
 

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

-Advertisement-
Play Games
更多相關文章
  • 選擇器(selector)是CSS中很重要的概念,所有HTML語言中的標記都是通過不同的CSS選擇器進行控制的。用戶只需要通過選擇器對不同的HTML標簽進行控制,並賦予各種樣式聲明,即可實現各種效果。 1. * * { margin: 0; padding: 0; } 星號選擇器用於選取頁面中的所有
  • 近日在群里看到有個題目,拿出來寫寫, 要求: 用html,css,原生js實現如圖的效果,先正向輸出,然後逆向回溯,最後停留在完整的畫面。 首先: HTML部分代碼: <div id="result"></div> 就是這麼簡單一行搞定。 CSS代碼: #result{ width:550px; m
  • [1]定義 [2]方法 [3]相容
  • 1.原型鏈繼承:構造函數、原型和實例的關係:每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。確認原型和實例之間的關係用instanceof。 原型鏈繼承缺點:字面量重寫原型會中斷關係,使用引用類型的原型,並且子類型還無法給超類型傳遞參數 f
  • [1]定義 [2]觸發條件 [3]作用
  • 一、表單標簽form 表單標簽用於申明表單,定義採集數據的範圍,即<form>包含的數據將被提交到資料庫上,包含了處理表單數據所用CGI程式的URL以及數據提交到伺服器的方法。 表單能夠包含 input 元素,比如文本欄位、覆選框、單選框、提交按鈕等等。還可以包含 menus、textarea、fi
  • 一、基本表格: 表格標記<table>,行標記<tr>,單元格標記<td> 基本語法: <table> <tr> <td>單元格內文字</td> <td>單元格內文字</td> ...... </tr> <tr> <td>單元格內文字</td> <td>單元格內文字</td> ...... </tr
  • css3出現之前,我們實現一個對象的一組連續動畫需要通過JavaScript或Jquery編寫,腳本代碼較為複雜; 若需要實現傾斜、旋轉之類的動畫難度將更高(我還沒試過用JavaScript或Jquery如何實現),而且即使能實現估計花的時間代價及維護難度是很大的,很多時候只能依靠畫圖工具製作此類動
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...