underscore.js源碼解析(二)

来源:http://www.cnblogs.com/WhiteBlade/archive/2016/02/27/5222493.html
-Advertisement-
Play Games

針對underscore.js封裝的方法進行具體的分析


前幾天我對underscore.js的整體結構做了分析,今天我將針對underscore封裝的方法進行具體的分析,代碼的一些解釋都寫在了註釋里,那麼廢話不多說進入今天的正文。

沒看過上一篇的可以猛戳這裡:underscore.js源碼解析(一)

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

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

_.each

 1   _.each = _.forEach = function(obj, iteratee, context) {
 2     //optimizeCb( )是underscore內部用來執行函數的很重要的方法,這個我們後面再聊
 3     iteratee = optimizeCb(iteratee, context);
 4     var i, length;
 5     if (isArrayLike(obj)) {
 6       //數組
 7       for (i = 0, length = obj.length; i < length; i++) {
 8         iteratee(obj[i], i, obj);
 9       }
10     } else {
11       //對象處理,這個_.keys( )我們也後面再聊
12       var keys = _.keys(obj);
13       for (i = 0, length = keys.length; i < length; i++) {
14         iteratee(obj[keys[i]], keys[i], obj);
15       }
16     }
17     return obj;
18   };

 
_.each結構很清晰,如果是數組,就遍曆數組調用相應的處理方法,如果是對象的話,就遍歷對象調用相應的處理方法。

其中判斷是否為數組的代碼如下:

1   var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
2   //獲取"length"屬性
3   var getLength = property('length');
4   //判斷是否是數組
5   var isArrayLike = function(collection) {
6     var length = getLength(collection);
7     return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
8   };
這種方法還是可以學習借鑒一下的。   接下來我們來聊上面提到的optimizeCb(),它是underscore內部用來執行函數的很重要的方法,並且改變所執行函數的作用域。

optimizeCb

 1   var optimizeCb = function(func, context, argCount) {
 2     if (context === void 0) return func;
 3     //argCount為函數參數的個數,針對不同參數個數進行不同的處理
 4     switch (argCount == null ? 3 : argCount) {
 5      //為單值的情況,例如times函數
 6       case 1: return function(value) {
 7         return func.call(context, value);
 8       };
 9       //因為2個參數的情況沒用被用到,所以在新版中被刪除了
10       //3個參數用於一些迭代器函數,例如map函數
11       case 3: return function(value, index, collection) {
12         return func.call(context, value, index, collection);
13       };
14      // 4個參數用於reduce和reduceRight函數
15       case 4: return function(accumulator, value, index, collection) {
16         return func.call(context, accumulator, value, index, collection);
17       };
18     }
19     return function() {
20       return func.apply(context, arguments);
21     };
22   };

 cb和_.iteratee

 1   var cb = function(value, context, argCount) {
 2    //如果為空,則返回value本身(identity函數就是一個返回本身的函數 )
 3     if (value == null) return _.identity;
 4     //如果為函數,則改變所執行函數的作用域
 5     if (_.isFunction(value)) return optimizeCb(value, context, argCount);
 6     //如果是對象,判斷是否匹配(matcher是一個用來判斷是否匹配的,我們具體後續再聊)
 7     if (_.isObject(value)) return _.matcher(value);
 8     return _.property(value);
 9   };
10   // 通過調用cb函數,生成每個元素的回調
11   _.iteratee = function(value, context) {
12     return cb(value, context, Infinity);
13   };

_.keys

 1   _.keys = function(obj) {
 2     //如果不是對象,返回空數組
 3     if (!_.isObject(obj)) return [];
 4     //如果支持原生的方法,就調用原生的keys方法
 5     if (nativeKeys) return nativeKeys(obj);
 6     var keys = [];
 7     //記錄所有屬性名
 8     for (var key in obj) if (_.has(obj, key)) keys.push(key);
 9     // IE9以下枚舉bug的相容處理
10     if (hasEnumBug) collectNonEnumProps(obj, keys);
11     return keys;
12   };
獲取所有的屬性名存在數組當中。 這裡的in操作符不僅在對象本身里查找,還會在原型鏈中查找。   其中IE9以下枚舉bug相容處理源碼如下:
 1 //判斷是否存在枚舉bug
 2   var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
 3   //不可枚舉的屬性如下
 4   var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',  'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
 5   
 6   var collectNonEnumProps = function(obj, keys) {
 7     var nonEnumIdx = nonEnumerableProps.length;
 8     var constructor = obj.constructor;
 9     var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
10 
11     // Constructor單獨處理部分.
12     var prop = 'constructor';
13     如果對象和keys都存在constructor屬性,則把他存入keys數組當中
14     if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
15 
16     while (nonEnumIdx--) {
17       prop = nonEnumerableProps[nonEnumIdx];
18       //如果obj對象存在上面數組裡那些不可枚舉的屬性但是不在原型中,並且keys數組裡面也沒有的話
19       if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)){
20         //將其添加進來
21         keys.push(prop);
22       }
23     }
24   };

 
既然都說到了keys那麼順帶著也介紹一下allkeys吧。

_.allKeys

1   _.allKeys = function(obj) {
2     if (!_.isObject(obj)) return [];
3     var keys = [];
4     //獲取所有的key
5     for (var key in obj) keys.push(key);
6     // 依然是IE9以下枚舉bug的相容處理
7     if (hasEnumBug) collectNonEnumProps(obj, keys);
8     return keys;
9   };


其實keys和allKeys代碼對比就少了if (_.has(obj, key)),allKeys是獲取所有的,包括繼承的

在介紹_.matcher之前,需要先介紹一下createAssigner 和_.isMatch。

createAssigner

 1 var createAssigner = function(keysFunc, defaults) {
 2     return function(obj) {
 3       var length = arguments.length;
 4       //判斷是否是對象
 5       if (defaults) obj = Object(obj);
 6       //如果一個參數或者對象為空,則返回這個對象
 7       if (length < 2 || obj == null) return obj;
 8       //從第二個參數開始
 9       for (var index = 1; index < length; index++) {
10         var source = arguments[index],
11             //獲取對應的keys
12             //keysFunc只有keys和allKeys兩種,在下麵_.extend和_.extendOwn中可以看到
13             keys = keysFunc(source),
14             l = keys.length;
15         //進行拷貝處理
16         for (var i = 0; i < l; i++) {
17           var key = keys[i];
18           //defaults是為了對defaults做單獨處理而添加的參數,具體的解釋_.defaults里做詳細分析
19           //在_.extend和_.extendOwn中default沒有傳值所以是underfinded,所以下麵判斷條件橫為true,正常進行拷貝處理
20           if (!defaults || obj[key] === void 0) obj[key] = source[key];
21         }
22       }
23       return obj;
24     };
25   };
1 //把後面的source拷貝到第一個對象
2   _.extend = createAssigner(_.allKeys);
3 
4   //把後面的source拷貝到第一個對象
5   // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
6   _.extendOwn = _.assign = createAssigner(_.keys);
7 
8   //對象中屬性值為underfined的屬性進行拷貝填充
9   _.defaults = createAssigner(_.allKeys, true);
把createAssigner中處理defaults的代碼拿到這裡具體分析  
if (!defaults || obj[key] === void 0) obj[key] = source[key];

 

此時defaults參數為true,所以就要看obj[key] === void 0這句了,void 0返回的就是underfinded,那麼這句就是判斷這個對象中屬性值為underfinded時,進行拷貝處理。

_.isMatch

 1 //用來判斷該屬性是否在對象中 (包括原型鏈)
 2 _.isMatch = function(object, attrs) {
 3     var keys = _.keys(attrs), length = keys.length;
 4     //判斷對象是否為空
 5     if (object == null) return !length;
 6     //判斷是否是對象
 7     var obj = Object(object);
 8     for (var i = 0; i < length; i++) {
 9       var key = keys[i];
10       //如果兩者值不等或者不在屬性不在對象當中則返回false
11       if (attrs[key] !== obj[key] || !(key in obj)) return false;
12     }
13     return true;
14   };

 _.matcher和_.matches

1 // 判斷對象是否匹配attrs的屬性
2   _.matcher = _.matches = function(attrs) {
3     //進行拷貝
4     attrs = _.extendOwn({}, attrs);
5     return function(obj) {
6       //用來判斷該屬性是否在對象中,上文有提及
7       return _.isMatch(obj, attrs);
8     };
9   };

小結

今天介紹了underscore.js中部分封裝函數,其他的會在後面的文章繼續一一分析

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

參考資料

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

 


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

-Advertisement-
Play Games
更多相關文章
  • 重要程度:★★★☆☆ 一、什麼是模板方法模式 使用了JAVA的繼承機制,在抽象類中定義一個模板方法,該方法引用了若幹個抽象方法(由子類實現)或具體方法(子類可以覆蓋重寫); 二、補充說明 其實就是JAVA的繼承以及抽象方法、重寫覆蓋的使用; 一般把模板方法定義成final避免被子類修改 三、角色 抽
  • 定義:一個對象應該對其他對象保持最少的瞭解。 問題由來:類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。 解決方案:儘量降低類與類之間的耦合。
  • 重要程度:★★★☆☆ 一、什麼是責任鏈模式 為請求創建了一個接收者對象的鏈,每個接收者都包含對另一個接收者的引用,當某個接受者不能處理該請求時,會將該請求轉給下一個接受者處理; 二、補充說明 請求發送者與請求接受者解耦 應用例子:struts的攔截器,servlet的過濾器 三、角色 抽象請求處理者
  • •低層模塊儘量都要有抽象類或介面,或者兩者都有。 •變數的聲明類型儘量是抽象類或介面。 •使用繼承時遵循里氏替換原則。
  • 在CSS網頁開發佈局中,需要對浮動和定位有深刻的理解才能在開發中游刃有餘。基於此,在博客園中做了本篇總結,這些總結來自實踐經驗和閱讀一些書籍後的理解總結。主要內容為浮動,清除浮動,定位。
  • 編者註:作者以一個運動的小車為例子,講述了三種實現HTML5動畫的方式,思路清晰,動畫不僅僅是canvas,還有css3和javascript.通過合理的選擇,來實現最優的實現。 PS:由於顯卡、錄製的幀間隔,以及可能你電腦處理器的原因,播放過程可能有些不太流暢或者失真! 分三種方式實現: (1)
  • 項目中需要一個下載功能,根據系統跳轉到不同的頁面,如iphone跳轉到IOS頁面,android跳轉到android頁面。 下麵為頁面判斷頁面: <!DOCTYPE HTML> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>手機AP
  • 對象屬性的標簽 value(屬性值), writable(屬性可寫), enumerable(屬性可枚舉), configurable(屬性可配置), 這些屬性標簽使對象所持有的屬性體現出不同的特性, 以便開發者開發使用. value value, 屬性的值, 這個屬性是最直觀的體現, 一個屬性我們
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...