Underscore源碼分析
幾年前就有人說(shuō)javascript是最被低估一種編程語(yǔ)言,自從nodejs出來(lái)后,全端(All Stack/Full Stack)概念日漸興起,現(xiàn)在恐怕沒(méi)人再敢低估它了。javascrip是一種類C的語(yǔ)言,有C語(yǔ)言基礎(chǔ)就能大體理解javascript的代碼,但是作為一種腳本語(yǔ)言,javascript的靈活性是C所遠(yuǎn)遠(yuǎn)不及的,這也會(huì)造成學(xué)習(xí)上的一些困難。
一、集合
1.首先是幾個(gè)迭代的方法。
_.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); } } // 鏈?zhǔn)秸{(diào)用 return obj; };
ES為數(shù)組同樣添加了原生的forEach()方法。不同的是這里的each(forEach)方法可以對(duì)所有集合使用,函數(shù)接受三個(gè)參數(shù)(集合、迭代函數(shù)、執(zhí)行環(huán)境)。
optimizeCb函數(shù)根據(jù)迭代函數(shù)參數(shù)個(gè)數(shù)的不同為不同的迭代方法綁定了相應(yīng)的執(zhí)行環(huán)境,forEach迭代函數(shù)同樣接受三個(gè)參數(shù)(值,索引,集合)。
接下來(lái)就是for循環(huán)調(diào)用迭代函數(shù)了。
_.map中一種更優(yōu)雅的判斷isArrayLike的實(shí)現(xiàn)方式:(只用一個(gè)for循環(huán))
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; // 合理使用&&、||、?:可以大大減少代碼量
還有兩個(gè)特別的地方:
•將集合分成了類數(shù)組集合和對(duì)象集合。使用了isArrayLike函數(shù):
// js的最大精確整數(shù) var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var isArrayLike = function(collection) { var length = collection != null && collection.length; return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; }; // 如果集合有Length屬性且為數(shù)字并且大于0小于最大的精確整數(shù),則判定是類數(shù)組
•使用了_.keys函數(shù),Object同樣有原生的keys函數(shù),用于返回一個(gè)集合obj可被枚舉的屬性數(shù)組。實(shí)現(xiàn)比較簡(jiǎn)單,for in加上hasOwnProperty()方法。
--------------------------------------------------------------------------------
_.map,_.reduce方法原理類似.
_.find函數(shù)和Array.some()類似,不同的是返回的是第一個(gè)使迭代結(jié)果為真的那個(gè)元素,而不是Array.some()那樣返回布爾值。
_.find = _.detect = function(obj, predicate, context) { var key; if (isArrayLike(obj)) { key = _.findIndex(obj, predicate, context); } else { key = _.findKey(obj, predicate, context); } if (key !== void 0 && key !== -1) return obj[key]; }; function createIndexFinder(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); var length = array != null && array.length; // 如果dir為1,index為0,index+=1,index正序循環(huán) // 如果dir 為-1,index為length-1,index += -1反序循環(huán) // 判斷循環(huán)條件則用了index >= 0 && index < length方法兼顧兩種循環(huán)方式 var index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { if (predicate(array[index], index, array)) return index; } return -1; }; } _.findIndex = createIndexFinder(1); _.findLastIndex = createIndexFinder(-1);
值得借鑒的地方是這里的一個(gè)for循環(huán)能夠根據(jù)傳入的參數(shù)不同配置不同的循環(huán)順序。
1.集合中的其他方法基本都是基于迭代方法來(lái)實(shí)現(xiàn)的。
_.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = cb(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; };
max方法用于尋找集合中的最大值,通過(guò)循環(huán)list中的所有項(xiàng),然后比較當(dāng)前項(xiàng)和結(jié)果項(xiàng),如果當(dāng)前項(xiàng)大于結(jié)果,則將其賦給結(jié)果項(xiàng),最后返回結(jié)果項(xiàng)。
2.集合轉(zhuǎn)換為數(shù)組
_.toArray = function(obj) { if (!obj) return []; // 如果是數(shù)組,采用了Array.prototype.slice.call(this,obj)這種方法 if (_.isArray(obj)) return slice.call(obj); // 類數(shù)組對(duì)象,這里沒(méi)有采用Slice方法,而是利用.map對(duì)集合進(jìn)行迭代,從而返回一個(gè)數(shù)組。 _.identity該方法傳入的值和返回的值相等。(主要用于迭代) if (isArrayLike(obj)) return _.map(obj, _.identity); // 普通對(duì)象,則返回由屬性值組成的數(shù)組。 return _.values(obj); };
數(shù)據(jù)類型
STL需要對(duì)vector、list等進(jìn)行區(qū)分是因?yàn)椴煌臄?shù)據(jù)結(jié)構(gòu)需要或者可以進(jìn)行不同的實(shí)現(xiàn),但underscore里面Collections和Arrays分開(kāi)是什么道理呢?這也要從javascript的數(shù)據(jù)類型說(shuō)起,看下圖。
- Underscore之Array_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- underscore之Collections_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- underscore之Chaining_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- JavaScript之underscore_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- 微信小程序使用第三方庫(kù)Underscore.js步驟詳解
- 深入解析Backbone.js框架的依賴庫(kù)Underscore.js的作用
- Underscore.js 1.3.3 中文注釋翻譯說(shuō)明
- Underscore.js 的模板功能介紹與應(yīng)用
- underscore之function_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
相關(guān)文章
關(guān)于 byval 與 byref 的區(qū)別分析總結(jié)
關(guān)于 byval 與 byref 的區(qū)別分析總結(jié)...2007-10-10深入理解JavaScript系列(11) 執(zhí)行上下文(Execution Contexts)
本章我們要講解的是ECMAScript標(biāo)準(zhǔn)里的執(zhí)行上下文和相關(guān)可執(zhí)行代碼的各種類型2012-01-01基于 Immutable.js 實(shí)現(xiàn)撤銷重做功能的實(shí)例代碼
這篇文章主要介紹了基于 Immutable.js 實(shí)現(xiàn)撤銷重做功能及一些需要注意的地方,需要的朋友可以參考下2018-03-03angular bootstrap timepicker TypeError提示怎么辦
這篇文章主要介紹了angular bootstrap timepicker TypeError提示的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06解讀IE和firefox下JScript和HREF的執(zhí)行順序
2008-01-01