摘要:今天要講的是,如何在數(shù)組中尋找元素,對(duì)應(yīng)中的,,,以及方法。如果往一個(gè)有序數(shù)組中插入元素,使得數(shù)組繼續(xù)保持有序,那么這個(gè)插入位置是這就是這個(gè)方法的作用,有序,很顯然用二分查找即可。
Why underscore
(覺(jué)得這部分眼熟的可以直接跳到下一段了...)
最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。
閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像和一個(gè)個(gè)大師對(duì)話,你會(huì)學(xué)到很多。為什么是 underscore?最主要的原因是 underscore 簡(jiǎn)短精悍(約 1.5k 行),封裝了 100 多個(gè)有用的方法,耦合度低,非常適合逐個(gè)方法閱讀,適合樓主這樣的 JavaScript 初學(xué)者。從中,你不僅可以學(xué)到用 void 0 代替 undefined 避免 undefined 被重寫(xiě)等一些小技巧 ,也可以學(xué)到變量類(lèi)型判斷、函數(shù)節(jié)流&函數(shù)去抖等常用的方法,還可以學(xué)到很多瀏覽器兼容的 hack,更可以學(xué)到作者的整體設(shè)計(jì)思路以及 API 設(shè)計(jì)的原理(向后兼容)。
之后樓主會(huì)寫(xiě)一系列的文章跟大家分享在源碼閱讀中學(xué)習(xí)到的知識(shí)。
underscore-1.8.3 源碼解讀項(xiàng)目地址 https://github.com/hanzichi/underscore-analysis
underscore-1.8.3 源碼全文注釋 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.js
underscore-1.8.3 源碼解讀系列文章 https://github.com/hanzichi/underscore-analysis/issues
歡迎圍觀~ (如果有興趣,歡迎 star & watch~)您的關(guān)注是樓主繼續(xù)寫(xiě)作的動(dòng)力
題外話先說(shuō)點(diǎn)題外話。
自從 5 月 16 日開(kāi)始 underscore 系列解讀文章,目前已經(jīng)收獲了 160+ star,在這里子遲也感謝大家的支持,并將繼續(xù)努力分享源碼里的干貨。有朋友私信我說(shuō)好幾天沒(méi)看到更新,在此也請(qǐng)大家原諒,畢竟我把它當(dāng)成了今年的計(jì)劃之一,而且平時(shí)也要上班工作,只能利用閑暇時(shí)間,而且樓主本人對(duì)文章的質(zhì)量要求比較高,如果是一律的流水文章,讀者學(xué)不到什么東西,自己的那關(guān)都過(guò)不了。其實(shí)如果有心,應(yīng)該能發(fā)現(xiàn) underscore-1.8.3 源碼全文注釋 一直有在更新(注釋行數(shù)已經(jīng)快破 1000 了)。
Main言歸正傳,上一章 中我們結(jié)束了 Object 擴(kuò)展方法部分,今天開(kāi)始來(lái)解讀 Array 部分的擴(kuò)展方法。其實(shí) JavaScript 中的數(shù)組是我最喜歡的類(lèi)型,能模擬棧、隊(duì)列等數(shù)據(jù)結(jié)構(gòu),還能隨意插入元素(splice),非常的靈活,這點(diǎn)做過(guò) leetcode 的應(yīng)該都深有體會(huì)(這里也順便安利下我的 leetcode 題解 Repo https://github.com/hanzichi/leetcode)。
今天要講的是,如何在數(shù)組中尋找元素,對(duì)應(yīng) underscore 中的 _.findIndex,_.findLastIndex,_.indexOf,_.lastIndexOf 以及 _.sortIndex 方法。
等等,是不是有點(diǎn)眼熟,沒(méi)錯(cuò),JavaScript 中已經(jīng)部署了 indexOf 方法(ES5)以及 findIndex 方法(ES6),這點(diǎn)不介紹了,大家可以自行學(xué)習(xí)。
我們先來(lái)看 _.findIndex 和 _.findLastIndex 函數(shù)。如果了解過(guò) Array.prototype.findIndex() 方法,會(huì)非常容易。_.findIndex 的作用就是從一個(gè)數(shù)組中找到第一個(gè)滿足某個(gè)條件的元素,_.findLastIndex 則是找到最后一個(gè)(或者說(shuō)倒序查找)。
舉個(gè)簡(jiǎn)單的例子:
var arr = [1, 3, 5, 2, 4, 6]; var isEven = function(num) { return !(num & 1); }; var idx = _.findIndex(arr, isEven); // => 3
直接看源碼,注釋已經(jīng)寫(xiě)的非常清楚了。這里要注意這個(gè) predicate 函數(shù),其實(shí)就是把數(shù)組中的元素傳入這個(gè)參數(shù),返回一個(gè)布爾值。如果返回 true,則表示滿足這個(gè)條件,如果 false 則相反。
// Generator function to create the findIndex and findLastIndex functions // dir === 1 => 從前往后找 // dir === -1 => 從后往前找 function createPredicateIndexFinder(dir) { // 經(jīng)典閉包 return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); // 根據(jù) dir 變量來(lái)確定數(shù)組遍歷的起始位置 var index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { // 找到第一個(gè)符合條件的元素 // 并返回下標(biāo)值 if (predicate(array[index], index, array)) return index; } return -1; }; } // Returns the first index on an array-like that passes a predicate test // 從前往后找到數(shù)組中 `第一個(gè)滿足條件` 的元素,并返回下標(biāo)值 // 沒(méi)找到返回 -1 // _.findIndex(array, predicate, [context]) _.findIndex = createPredicateIndexFinder(1); // 從后往前找到數(shù)組中 `第一個(gè)滿足條件` 的元素,并返回下標(biāo)值 // 沒(méi)找到返回 -1 // _.findLastIndex(array, predicate, [context]) _.findLastIndex = createPredicateIndexFinder(-1);
接下來(lái)看 _.sortIndex 方法,這個(gè)方法無(wú)論使用還是實(shí)現(xiàn)都非常的簡(jiǎn)單。如果往一個(gè)有序數(shù)組中插入元素,使得數(shù)組繼續(xù)保持有序,那么這個(gè)插入位置是?這就是這個(gè)方法的作用,有序,很顯然用二分查找即可。不多說(shuō),直接上源碼。
// _.sortedIndex(list, value, [iteratee], [context]) _.sortedIndex = function(array, obj, iteratee, context) { // 注意 cb 方法 // iteratee 為空 || 為 String 類(lèi)型(key 值)時(shí)會(huì)返回不同方法 iteratee = cb(iteratee, context, 1); // 經(jīng)過(guò)迭代函數(shù)計(jì)算的值 var value = iteratee(obj); var low = 0, high = getLength(array); while (low < high) { var mid = Math.floor((low + high) / 2); if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; };
最后我們說(shuō)說(shuō) _.indexOf 和 _.lastIndexOf 方法。
ES5 引入了 indexOf 和 lastIndexOf 方法,但是 IE < 9 不支持,面試時(shí)讓你寫(xiě)個(gè) Polyfill,你會(huì)怎么做(可以把 underscore 的實(shí)現(xiàn)看做 Polyfill)?如何能讓面試官滿意?首先如果分開(kāi)來(lái)寫(xiě),即兩個(gè)方法相對(duì)獨(dú)立地寫(xiě),很顯然代碼量會(huì)比較多,因?yàn)閮蓚€(gè)方法功能相似,所以可以想辦法調(diào)用一個(gè)方法,將不同的部分當(dāng)做參數(shù)傳入,減少代碼量。其次,如果數(shù)組已經(jīng)有序,是否可以用更快速的二分查找算法?這點(diǎn)會(huì)是加分項(xiàng)。
源碼實(shí)現(xiàn):
// Generator function to create the indexOf and lastIndexOf functions // _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); // _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); function createIndexFinder(dir, predicateFind, sortedIndex) { // API 調(diào)用形式 // _.indexOf(array, value, [isSorted]) // _.indexOf(array, value, [fromIndex]) // _.lastIndexOf(array, value, [fromIndex]) return function(array, item, idx) { var i = 0, length = getLength(array); // 如果 idx 為 Number 類(lèi)型 // 則規(guī)定查找位置的起始點(diǎn) // 那么第三個(gè)參數(shù)不是 [isSorted] // 所以不能用二分查找優(yōu)化了 // 只能遍歷查找 if (typeof idx == "number") { if (dir > 0) { // 正向查找 // 重置查找的起始位置 i = idx >= 0 ? idx : Math.max(idx + length, i); } else { // 反向查找 // 如果是反向查找,重置 length 屬性值 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; } } else if (sortedIndex && idx && length) { // 能用二分查找加速的條件 // 有序 & idx !== 0 && length !== 0 // 用 _.sortIndex 找到有序數(shù)組中 item 正好插入的位置 idx = sortedIndex(array, item); // 如果正好插入的位置的值和 item 剛好相等 // 說(shuō)明該位置就是 item 第一次出現(xiàn)的位置 // 返回下標(biāo) // 否則即是沒(méi)找到,返回 -1 return array[idx] === item ? idx : -1; } // 特判,如果要查找的元素是 NaN 類(lèi)型 // 如果 item !== item // 那么 item => NaN if (item !== item) { idx = predicateFind(slice.call(array, i, length), _.isNaN); return idx >= 0 ? idx + i : -1; } // O(n) 遍歷數(shù)組 // 尋找和 item 相同的元素 // 特判排除了 item 為 NaN 的情況 // 可以放心地用 `===` 來(lái)判斷是否相等了 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { if (array[idx] === item) return idx; } return -1; }; } // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. // _.indexOf(array, value, [isSorted]) // 找到數(shù)組 array 中 value 第一次出現(xiàn)的位置 // 并返回其下標(biāo)值 // 如果數(shù)組有序,則第三個(gè)參數(shù)可以傳入 true // 這樣算法效率會(huì)更高(二分查找) // [isSorted] 參數(shù)表示數(shù)組是否有序 // 同時(shí)第三個(gè)參數(shù)也可以表示 [fromIndex] (見(jiàn)下面的 _.lastIndexOf) _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); // 和 _indexOf 相似 // 反序查找 // _.lastIndexOf(array, value, [fromIndex]) // [fromIndex] 參數(shù)表示從倒數(shù)第幾個(gè)開(kāi)始往前找 _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
這里有一點(diǎn)要注意,_.indexOf 方法的第三個(gè)參數(shù)可以表示 [fromIndex] 或者 [isSorted],而 _.lastIndexOf 的第三個(gè)參數(shù)只能表示 [fromIndex],我們從代碼中便可以輕易看出:
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
關(guān)于這點(diǎn)我也百思不得其解,不知道做這個(gè)限制是為了什么考慮,歡迎探討~
最后給出本文涉及的五個(gè)方法的源碼位置 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L613-L673
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86306.html
摘要:今天要講的是數(shù)組展開(kāi)以及和數(shù)組展開(kāi)息息相關(guān)的一個(gè)重要的內(nèi)部方法。也是個(gè)布爾值,當(dāng)為并且也為時(shí),能過(guò)濾參數(shù)元素中的非數(shù)組元素。首先并不需要對(duì)數(shù)組深度展開(kāi),其次傳入的是數(shù)組,對(duì)于非數(shù)組元素可以直接忽略。 Why underscore (覺(jué)得這一段眼熟的童鞋可以直接跳到正文了...) 最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 201...
摘要:最近開(kāi)始看源碼,并將源碼解讀放在了我的計(jì)劃中。將轉(zhuǎn)為數(shù)組同時(shí)去掉第一個(gè)元素之后便可以調(diào)用方法總結(jié)數(shù)組的擴(kuò)展方法就解讀到這里了,相關(guān)源碼可以參考這部分。放個(gè)預(yù)告,下一篇會(huì)暫緩下,講下相關(guān)的東西,敬請(qǐng)期待。 Why underscore 最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。 閱讀一些著名框架類(lèi)庫(kù)的源碼,就好...
摘要:最近開(kāi)始看源碼,并將源碼解讀放在了我的計(jì)劃中。今天就跟大家聊一聊中一些常用類(lèi)型檢查方法,以及一些工具類(lèi)的判斷方法。用是否含有屬性來(lái)判斷工具類(lèi)判斷方法接下來(lái)看下一些常用的工具類(lèi)判斷方法。 Why underscore 最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。 閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像和一個(gè)個(gè)大師對(duì)話...
摘要:最近開(kāi)始看源碼,并將源碼解讀放在了我的計(jì)劃中。后文中均假設(shè)比較的兩個(gè)參數(shù)為和。,如果和均是類(lèi)型或者類(lèi)型,我們可以用來(lái)判斷是否。 Why underscore 最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。 閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像和一個(gè)個(gè)大師對(duì)話,你會(huì)學(xué)到很多。為什么是 underscore?最主要的原...
摘要:而數(shù)組元素去重是基于運(yùn)算符的。而如果有迭代函數(shù),則計(jì)算傳入迭代函數(shù)后的值,對(duì)值去重,調(diào)用方法,而該方法的核心就是調(diào)用方法,和我們上面說(shuō)的方法一異曲同工。 Why underscore (覺(jué)得這部分眼熟的可以直接跳到下一段了...) 最近開(kāi)始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。 閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像...
閱讀 1286·2021-11-11 16:54
閱讀 1829·2021-10-13 09:40
閱讀 1010·2021-10-08 10:05
閱讀 3568·2021-09-22 15:50
閱讀 3794·2021-09-22 15:41
閱讀 2009·2021-09-22 15:08
閱讀 2418·2021-09-07 10:24
閱讀 3631·2019-08-30 12:52