如何用 JavaScript 將 [1,2,3,[4,5, [6,7]], [[[8]]]] 這樣一個 Array 變成 [1,2,3,4,5, 6,7,8] 呢?傳說中的 Array Flatten。
處理這種問題,通常我們會需要遞歸,來讓程序自己按照一種算法去循環。在某書說寫著,“遞歸是一種強大的編程技術”,好吧,她不僅僅屬於 JavaScript。遞歸可以很難,也可以比較簡單(總得來說還是比較難)。處理上面這個問題,用遞歸來解決,應該是比較適合的。之前工友這樣實現了,算是一個簡單的遞歸使用實例吧:
復制代碼 代碼如下:
flatten: function(ac){
var array = [];
var group = this.arr;
if(ac) group = ac;
for (var i = 0; i < group.length; i++){
if(group[i] instanceof Array){
array = array.concat(this.flatten(group[i]));
}else{
array = array.concat(group[i]);
}
}
return array;
}
在 if(group[i] instanceof Array) 的時候,調用函數自身,通過傳參數的形式進行遞歸。只是在重構 Array.js 的時候,就覺得既然是框架,那麼多抽象出來的東西不用,是不是太浪費了。所以,最好調用已經抽象出來的靜態函數,而不是又重新一遍。這裡有 for 循環,也就是說我們會需要有 each。結果呢?四個字,不好實現。因為我們始終要創建一個數組,最終 return 這個新的數組,得重新抽出來一個函數來調。這不就違背了初衷?
網上瞄了一下,最終盯在 prototype 上。他的實現方法是抽象出一個處理遞歸增量的函數,再利用這個函數來做遞歸。怎麼說呢?想說,這就叫框架。下面是一個處理遞歸的函數:
復制代碼 代碼如下:
function inject(memo, iterator, context) {
this.each(function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
}
而這個 flatten 函數,最終的實現是這樣的,這代碼真漂亮:
復制代碼 代碼如下:
function flatten() {
return this.inject([], function(array, value) {
if (Object.isArray(value))
return array.concat(value.flatten());
array.push(value);
return array;
});
}
當然,這裡面還需要另外一個抽象出來的函數,來處理 for 循環,就是我們的 each 函數了。順路在 flatten 中,帶出這個 each 函數吧,學習了 jQuery 的做法,加入原生支持;當然,還可以處理純對象,而不僅僅是數組:
復制代碼 代碼如下:
each: function (callback, bind) {
var isObject = arale.typeOf(this.obj) === 'object',
i = 0,
key;
if (isObject) {
var obj = this.obj;
for (key in obj) {
if (callback.call(bind, key, obj[key]) === false) {
break;
}
}
} else {
var arr = this.obj;
if (Array.prototype.forEach) {
// 用戶 return false; 的時候還會繼續執行
// 原生的很囧,去還是捨呢? marked TODO;
return [].forEach.call(arr, callback, bind);
};
for (var value = arr[0], length = arr.length; i < length && callback.call(bind, i, value) !== false; value = arr[++i]) {};
}
}
最近玩 Javascript 比較多。瞄了一下最近的文章,還有在團隊內部博客上發的文章,全都是 JS的。囧。似乎是一個很大的改變。需要平衡一下了。