最近是在所在實習公司的第一個sprint,有個朋友又請假了,因此任務比較重,一直這麼久都沒怎麼更新了,這個週末賴了個牀,糾結了一下子決定仍是繼續寫這個系列,雖然比較乏味,可是學到的東西仍是不少的。javascript
以前主要是針對函數處理部分的API作解讀,通過那些天的努力,基本已經解讀完了,如今把重點移到數組上。對於數組處理API的解讀,從這篇文章開始。java
flatten是一個很基礎的函數,在Underscore中也算是一個工具函數,爲了方便之後的講解,今天先閱讀flatten函數的源碼。git
首先,咱們帶着問題來閱讀源碼,若是你參加面試,考官讓你手寫一個展開數組的函數,你會怎麼寫?github
實現一個flatten函數
咱們接受的參數應該是一個數組,咱們能夠使用一個叫array的變量表示它,它的返回值應該是一個數組,使用result表示:面試
function flatten(array) { var result = []; // ... 展開代碼 return result }
而後咱們應該對傳入的數組進行類型驗證,若是不是數組,咱們應該拋出一個類型異常:數組
function flatten(array) { var result = []; if(Object.prototype.toString.call(array) !== '[object Array]') throw new TypeError('Please pass a array-type object as parameter to flatten function'); else { // ... 展開代碼 } return result }
這樣就能夠保證咱們接收到的參數是一個數組,接下來咱們應該遍歷array參數,對於它的每一項,若是不是數組,咱們就將其添加到result中,不然繼續展開:函數
function flatten(array) { var result = []; if(Object.prototype.toString.call(array) !== '[object Array]') throw new TypeError('Please pass a array-type object as parameter to flatten function'); else { for(var i = 0; i < array.length; i++) { if(Object.prototype.toString.call(array[i]) === '[object Array]') { // ... 繼續展開。 } else { result.push(array[i]); } } } return result }
當數組中的項仍是一個數組時,咱們應當如何展開呢? 因爲不肯定究竟是嵌套了多少層數組,因此最好是使用遞歸來展開,可是有新的問題,咱們的flatten函數返回一個數組結果,可是咱們如何把遞歸結果返回給咱們的result呢,是使用concat方法仍是怎樣?工具
因爲函數中對象類型的參數是引用傳值,因此咱們能夠把result傳遞給flatten自身,使其直接修改result便可:url
function flatten(array, result) { var result = result || []; if(Object.prototype.toString.call(array) !== '[object Array]') throw new TypeError('Please pass a array-type object as parameter to flatten function'); else { for(var i = 0; i < array.length; i++) { if(Object.prototype.toString.call(array[i]) === '[object Array]') { // ... 遞歸展開。 arguments.callee(array[i], result); } else { result.push(array[i]); } } } return result }
以上函數,就基本實現了flatten的功能,再美化一下:spa
var flatten = function(array, result) { var result = result || []; var length = array.length; var toString = Object.prototype.toString; var type = toString.call(array); if(type !== '[object Array]') throw new TypeError('The parameter you passed is not a array'); else { for(var i = 0; i < length; i++) { if(toString.call(array[i]) !== '[object Array]') { result.push(array[i]); } else { arguments.callee(array[i], result); } } } return result; }
你們能夠把上面這段代碼拷貝到控制檯進行實驗。
Underscore中的flatten函數
經過咱們本身親手實現一個flatten函數,閱讀Underscore源碼就變得簡單了。 下面是Underscore中flatten函數的源碼(附註釋):
var flatten = function (input, shallow, strict, output) { output = output || []; var idx = output.length; //遍歷input參數。 for (var i = 0, length = getLength(input); i < length; i++) { var value = input[i]; if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { // Flatten current level of array or arguments object. //若是input數組的元素是數組或者類數組對象,根據是否shallow來展開,若是shallow爲true,那麼只展開一級。 if (shallow) { var j = 0, len = value.length; while (j < len) output[idx++] = value[j++]; } else { //若是shallow爲false,那麼遞歸展開全部層級。 flatten(value, shallow, strict, output); idx = output.length; } } else if (!strict) { //若是value不是數組或類數組對象,而且strict爲false。 //那麼直接將value添加到輸出數組,不然忽略value。 output[idx++] = value; } } return output; };
Underscore實現的flatten更增強大,它支持類數組對象而不單單是數組,而且它多了兩個參數——shallow和strict。
當shallow爲true時,flatten只會把輸入數組的數組子項展開一級,若是shallow爲false,那麼會所有展開。
當strict爲false時,只要是非數組對象,flatten都會直接添加到output數組中;若是strict爲true,那麼會無視input數組中的非類數組對象。
更多Underscore源碼閱讀:GitHub