最近是在所在實習公司的第一個sprint,有個朋友又請假了,因此任務比較重,一直這麼久都沒怎麼更新了,這個週末賴了個牀,糾結了一下子決定仍是繼續寫這個系列,雖然比較乏味,可是學到的東西仍是不少的。git
以前主要是針對函數處理部分的API作解讀,通過那些天的努力,基本已經解讀完了,如今把重點移到數組上。對於數組處理API的解讀,從這篇文章開始。github
flatten是一個很基礎的函數,在Underscore中也算是一個工具函數,爲了方便之後的講解,今天先閱讀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中,不然繼續展開:spa
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方法仍是怎樣?prototype
因爲函數中對象類型的參數是引用傳值,因此咱們能夠把result傳遞給flatten自身,使其直接修改result便可:code
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的功能,再美化一下:對象
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;
}
複製代碼
你們能夠把上面這段代碼拷貝到控制檯進行實驗。
經過咱們本身親手實現一個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