Array.reduce()方法的使用
起因是學習異步函數的串行與並行寫法時,發現reduce方法可以簡化寫法,然後看到一篇博客裏面這樣一段代碼:
var array = [1, [2, [3, 4], 5], 6];
function flatten(array) {
return array.reduce(function (arr, item) {
return (Object.prototype.toString.call(item) === '[object Array]'
? Array.prototype.push.apply(arr, flatten(item))
: arr.push(item)
, arr);
}, []);
}
console.log(flatten(array));
發現好多不理解的地方,花費些許時間終於弄懂了:
reduce(fn,initValue)
接收2個參數。第一個是迭代器函數,函數的作用是對數組中從左到右的每一個元素進行處理。函數有4個參數,分別是accumulator、currentValue、currentIndex、array。
accumulator 累加器,即函數上一次調用的返回值。
第一次的時候爲initialValue || arr[0]
currentValue 數組中函數正在處理的的值
第一次的時候initialValue || arr[1]
currentIndex 數組中函數正在處理的的索引
array 函數調用的數組
initValuereduce
的第二個可選參數,累加器的初始值。沒有時,累加器第一次的值爲currentValue;return (a,b)
這裏其實就是逗號運算符了。先計算左邊,再計算右邊,最後返回右邊的值。比如var n = (1,2,3,4); console.log(n); //4
- 然後
reduce
的第一個參數的這個函數一定要每次都有返回值; Array.prototype.push.apply(arr1,arr2)
爲什麼可以把2個數組concat()
起來。
我覺得我應該能想的出來的,以前看過但沒怎麼用,印象不深刻,所以沒記起來。
這個從 call 、apply、bind 說起。
fn.call() 第一個參數,傳入調用的對象,第二個以後的參數爲fn的參數,一一對應;
fn.apply() 同上,第二個參數變爲數組,數組中的元素與fn的參數一一對應;
fn.bind() 同 apply ,不過 call apply 直接就執行了, bind 是生成了一個新函數,在需要的時候執行。
這樣就出來了,Array.prototype.push.apply(arr1,arr2)
將 arr2 中的每一個元素當成參數 push 到 arr1 中去了,而不是arr1.push(arr2)
。
所以上述的意思就是,第一次迭代時 accumulator 的值爲[]
,然後遞歸調用flatten,每次都返回accumulator
- 此外 apply 還有很多巧妙的用處,比如數組找最大值
Math.max.apply(Math,arr)
; - 僞數組的轉換
Array.prototype.slice.apply(arguments)
上文說過fn.apply()
接收2個參數,一個是context,一個是數組作爲參數。此時只傳了一個上下文環境(arguments
是僞數組,本身沒有slice方法,所以借用數組原型上的方法),而slice本身是沒有傳參數的。不傳參數默認爲slice(0)
,即Array.prototype.slice.apply(arguments,[0])
所以就將整個數組複製一份返回出來了。
關於promise
reduce 可以使 promise 的串行.then()
寫法變得簡單;
// promise生成函數
function log(n, delay, param) {
return function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(n);
resolve(param);
}, delay);
})
}
}
/**異步函數的串行1
* 這是正常的promise鏈式調用,每個then裏面都返回一個promise;
* 上一個執行完成,下一個纔會執行
*/
Promise.resolve()
.then(log(1,300))
.then(log(2,0))
.then(log(3,500))
.then(log(4,100));
上面的代碼按順序打印出1、2、3、4。
如果想讓1、2、3、4全部執行完畢後再執行某個函數,則可以用Promise.all(arr)
方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。具體示例如下:
//異步函數的並行寫法
Promise.all([
log(1, 300, "aa")(), log(2, 0, "bb")(), log(3, 1500, "cc")(), log(4, 100, "dd")()
]).then((result) => {
console.log('finished!')
console.log(result);
});
//2
//4
//1
//3
//finished!
//[ 'aa', 'bb', 'cc', 'dd' ]
如果想讓串行的寫法和這種並行的寫法差不多的話,可以用到reduce()
函數
//異步函數的串行2
//reduce中傳入Promise.resolve()作爲累加器的初始值
//第一次時Promise.resolve().then(fn)傳入一個函數log(1, 300),函數運行後返回promis實例
//這個實例作爲第二次的累加器的值,然後調用它的then()方法,並傳入數組的第二項...
[log(1, 300),log(2, 0),log(3, 500),log(4, 100)].reduce((accumulator, value) => {
return accumulator.then(value)
},Promise.resolve());
結果和上面的串行1一樣,按順序打印出來1、2、3、4