摘要:第四個判斷如果是對象執行返回一個斷言函數,用來判定傳入對象是否匹配指定鍵值屬性。都不匹配最后執行,返回傳入的對象的屬性。設置的值并生成函數,等同于,使具有屬性且有值則返回,否則返回,這是一個判斷函數。
在第二小章節里面我按照源碼順序介紹幾個方法,源碼緊接著第一章繼續:
var builtinIteratee;
builtinIteratee,內置的 Iteratee (迭代器)。
var cb = function(value, context, argCount) { if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value)) return _.matcher(value); return _.property(value); };
cb 函數接受三個參數,陸續四個判斷,第一個判斷 _.iteratee,根據 JAVASCRIPT 的上下文,首先 builtinIteratee 為 undefined,然 cb 函數內 builtinIteratee 為 undefined,接下來就是 _.iteratee = builtinIteratee 里面的 cb 函數,so...接著第二個判斷傳入參數是否為空值,如果是則返回 _.identity 函數,即當前傳入值。第三個判斷傳入值是方法則執行 optimizeCb 函數。第四個判斷如果是對象執行返回一個斷言函數,用來判定傳入對象是否匹配attrs指定鍵/值屬性。都不匹配最后執行 _.property,返回傳入的對象的 key 屬性。
_.iteratee = builtinIteratee = function(value, context) { return cb(value, context, Infinity); };
_.iteratee 這個函數一般認為是一個迭代器,這里是作者的主觀寫法,因為從意義上講, cb 函數和 _.iteratee 函數很相似,甚至說只要稍加改動 cb 完全可以替換掉 _.iteratee,作者用 _.iteratee 包裝 cb 并提供外部訪問,雖然實際工作中我們運用 _.iteratee 函數并不常見,但如果用的好絕對是一利器,由 underscore.js 源碼內部隨處可見的 cb(),就知道這一函數的作用之大。在 underscore 中 return cb() 傳入了第三個參數 Infinity,意為參數類型為 Infinity 當執行第三個 cb 函數的 if 判斷,執行 return optimizeCb(); 時就會發揮其作用,Infinity 類型也蠻有意思,有興趣的同學可以參考 Infinity、POSITIVE_INFINITY 和 NEGATIVE_INFINITY。
var restArgs = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = Math.max(arguments.length - startIndex, 0); var rest = Array(length); for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } args[startIndex] = rest; return func.apply(this, args); }; };
restArgs(其余的參數),什么意思呢,我們看它傳入了一個 function 和 一個 Number 類型的 startIndex 標識,首先處理的是 startIndex。三元運算判斷 startIndex 是否存在,是則為 +startIndex,否則為 func.length - 1 即傳入 function 中的傳入形參的數量減一,舉個例子如:
var aFunction = function(a,b,c){}; function(a){ console.log(a.length) //3 }
這么做的目的是什么呢,我們都知道在一個 Array 中數組排序是從 0 開始,所以就不難理解 func.length - 1,但是 +startIndex 又是為什么呢,答案是同樣是考慮數組排序是從 0 開始。其實在源碼中 restArgs 這個內部函數作者還并沒有用到過 startIndex 這個參數,如果需要使用那么它的意義在于 return function 的時候處理 function 中的一部分參數,我們現在假設使用了 startIndex 參數,如果 startIndex >2 即拋去 arguments[startIndex + 1] 作為傳入參數的一步限定,然后將 arguments[arguments.length - startIndex + 1] ~ arguments[arguments.length] 封裝數組作為 arguments[startIndex] 傳入,當然這過程中需要將 arguments[arguments.length - startIndex + 1] ~ arguments[arguments.length] 從 arguments 刪除,所以源碼中運用了多個 Array 用于這一過程其目的就是重組 arguments。而當 0
前面說到作者并沒有使用 startIndex 這個參數,那么沒有 startIndex 是什么情況呢,startIndex = func.length - 1 就是說設定 Array 的長度即 arguments 的長度,我們可以看到作者對 restArgs 這個函數很重視,并且好像一直在優化它,作者想要做什么也不得而知,畢竟拋開 startIndex 的話:
var restArgs = function(func) { startIndex = func.length - 1; return function() { var rest = Array(1); rest[0] = arguments[startIndex]; var args = Array(arguments.length); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } args[startIndex] = rest; return func.apply(this, args); }; };
等同于:
var restArgs = function(func) { return function() { return func.apply(this, arguments); }; };
作者將5行代碼擴展到21行,其實就是為了一個 startIndex 而已。
var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; if (nativeCreate) return nativeCreate(prototype); Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; };
baseCreate 用于創建一個干凈且只存在具有想要其具有 prototype 的函數,第一個判斷是否具有 prototype 參數,第二個判斷運用 Object.create 創建,余下則是自己運用 Ctor 這個空函數創建,沒什么可細說的。
var property = function(key) { return function(obj) { return obj == null ? void 0 : obj[key]; }; };
property 用于獲取 obj 的 key 值,通過 property() 設置 key ,重點是設置兩個字,有 key 則以沒有則創建之。
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
設置 一個最大值 MAX_ARRAY_INDEX,Math.pow(2, 53) - 1 意為2的53次冪等于9007199254740991,Math 的相關函數參考 Math,其實我一直覺得 MAX_ARRAY_INDEX 并不用設置這么大的值,Math.pow(2, 16) 就足以。
var getLength = property("length");
設置 obj 的 key 值并生成函數,等同于:
var getLength = function(obj) { return obj == null ? void 0 : obj["length"]; };
var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX; };
isArrayLike,使 Obj 具有 length 屬性且有值則返回 true,否則返回 false,這是一個判斷函數。
_.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; };
我一直以為 JAVASCRIPT 最精華的就是回調的執行方式,雖然互聯網上一些文章總在說回調毀了一切,人云亦云等等,但是回調支撐起了所有的框架,而且回調很優雅用的好可以很舒服,回調不是毀了一切只是因為某些人不恰當的設置回調毀了他自己的代碼。在 _.forEach 中 iteratee 即回調函數,其中運用了 optimizeCb 優化回調,然后是一個常規判斷,這里為什么用 isArrayLike(obj) 而不是 isArray(obj) 來判斷是不是數組呢,留下一個思考問題。
_.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; };
封裝 map 函數,沒什么好說的,參考 Map、Map.prototype、WeakMap 用于知識儲備,至于作者的 _.map 更多的是根據一定的條件遍歷 obj 中的元素,與 _.forEach 的更大區別是 _.forEach 不會對傳入的 obj 做改動直接 return obj,而 _.map 會 return results,return results 是每個 iteratee 回調的集合。
var createReduce = function(dir) { var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; if (!initial) { memo = obj[keys ? keys[index] : index]; index += dir; } for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; return function(obj, iteratee, memo, context) { var initial = arguments.length >= 3; return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); }; };
createReduce,創建 reduce。關于 reduce 的介紹可見 reduce 方法 (Array) (JavaScript):https://msdn.microsoft.com/library/ff679975(v=vs.94).aspx 和 array-reduce,作者這里的 reduce 肯定不是這樣,但既然命名為 createReduce,想來也脫不了太多關系。函數中 reducer 首先定義 keys,其值為 obj 的 key 集合或者 false,后面幾個語句里都有對于 keys 的三元運算,目的就是排除 obj 不為 Object 的可能性。接下來判斷傳入 initial,如果傳入 initial 為 false 則默認 memo 值為 keys[keys.length-1] || 0,之后是 for 循環遍歷回調,并返回最后一個回調值。跳出 reducer 函數 return function 的恰恰是引用 reducer 函數的外部接口,于是所有一切都連貫上了,包括 initial 的定義是 arguments 長度大于等于3等等。
我們再重新過一遍代碼,在最外部 return 的時候判斷 initial,實際上就是再確定是否傳入了 memo 和 context,當然最主要的就是 memo,以此來確定在內部 reducer 的時候是否具有初始值。在這里我覺得作者應該對 memo 進行類型判斷的,如果是 Number 或者 String 還說的過去,但是如果傳入 memo 是 Object 就有點說不過去了,會出錯的。比如:
_.reduce([1, 2, 3], function(memo, num){ return memo + num; }); 6 _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 1); 7 _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, "1"); "1123" _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, []); "123" _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, [1,2]); "1,2123" _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, {a:1}); "[object Object]123"
_.reduce = _.foldl = _.inject = createReduce(1);
這里就是用 createReduce 包裝好的 _.reduce,不解釋。
_.reduceRight = _.foldr = createReduce(-1);
這里就是用 createReduce 包裝好的 _.reduceRight,與 _.reduce 計算順序相反即從右面向左面開始。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86314.html
摘要:組件的選擇命令行工具首先我們需要一個命令行工具來方便的執行命令,這里我們選擇組件,如果不喜歡使用且有能力的人完全可以通過組件自己封裝執行命令函數。 對于一個成熟的項目而言,一定需要一個注釋文檔生成工具,我們有很多可選的開源項目,如jsdoc、yuidocjs 等等,擁有這些強大的工具我們完全可以勝任任何注釋方面的管理了么? 一個成熟的開發者都會知道不管怎么樣的項目都會在不同的開發條件下...
摘要:新出臺的則規定,包括六種原始類型和,還有一種,詳見數據類型和數據結構。用于返回一個由給定對象的所有可枚舉自身屬性的屬性名組成的數組,。接下來判斷數字進行相應的操作,其中有和兩個方法,詳見和。 一直想寫一篇這樣的文章,于是心動不如行動,這里選擇的是 Underscore.js 1.8.3 版本,源碼注釋加在一起1625行。 Underscore.js 1.8.3 http://unde...
摘要:用來構成和兩個函數,主要針對的是為了將函數調用模式更改為構造器調用和方法調用。通過函數設定時間為毫秒后執行函數的回調函數,用以達到在規定時間毫秒時執行函數的目的,并且規定時間內只執行一次函數。 北京的雨已經斷斷續續下了好久,昏昏欲睡的躲在家里不愿意出門,火影忍者快要結束了,一拳超人第二季據說還要等好多年,勇者大冒險貌似斷更了,我又是在不喜歡海賊王的畫風,所以,我該看什么好呢。 va...
摘要:接收三個參數分別為回調和,其中與是可選參數。官網釋義排序一個列表組成一個組,并且返回各組中的對象的數量的計數。類似,但是不是返回列表的值,而是返回在該組中值的數目。 繼續前面的內容,前文我們提到了很多方法的講解,其實到這里就已經差不多了,因為大部分代碼其實都是套路,一些基礎函數再靈活變化就可以組成很多實用的功能。 _.sortBy = function(obj, iteratee,...
摘要:傳入值進行判斷以此決定函數,將三個參數包括回調傳入中其中回調函數充當迭代器進行真值檢測,最后。是從一個中隨機返回值,并且返回值受限于這個參數,如果沒有傳入或者傳入了則執行語句,目的是將判斷處理之后返回單一值。 今天繼續上次的內容,之前我們講到了 reduce 的用法,其實我覺得用法倒是其次的關鍵是作者實現 reduce 過程中所靈活用到的函數處理方法,我們只要有心稍加總覺完全可以拿來主...
閱讀 2624·2021-11-18 10:07
閱讀 1083·2021-08-03 14:04
閱讀 726·2019-08-30 13:08
閱讀 2579·2019-08-29 15:33
閱讀 1087·2019-08-29 14:07
閱讀 2985·2019-08-29 14:04
閱讀 1435·2019-08-29 11:19
閱讀 1144·2019-08-29 10:59