reduce能做什么?
1)求和
2)計算價格
3)合並數據
4)redux的compose方法
這篇文章主要內容是什么?
1)介紹reduce的主要作用
2)手寫實現reduce方法
0)了解reduce
在了解reduce的好處之前,我們先知道reduce是什么? reduce是數組的一個方法,函數接受的參數有四個,函數上一次的結果,當前的結果,索引,當前執行的數組;在尾巴處也可以加一個初始的值。每一個參數都有很大的用處,運用好的話,可以幻化出各種變化。
let r = [1,2,3].reduce(('上一次返回的值','當前的值','當前索引','執行的數組') => {
},'第一次的初始值')
1)求和
這個函數執行倆次,第一次a = 1,b = 2,。a+b的結果賦予下一次的a 。 第二次a = 3,b = 3。 運算結束,放回總結果。
// 求和 let r = [1, 2, 3].reduce((a, b) => { return a + b; }) console.log(r) // r = 6
2)計算價格
這個是遍歷后台傳過來的價格和數量,有時候結構是比較復雜的。這里我們看看第一個,這樣是不對的。第一次 100*2+100*2 . 將結果賦予a 那下一次執行 , a.price 和 a.number 都找不到了,都為undefined。所以我們需要一個初始值,將所有的結果累加在初始值上。這里我們看看第二個
函數,給a加個初始值0,也就是在reduce的尾巴加個0, 這樣每次b的結果累加在a上。那么價格就可以計算出來了。
// 算價格 let k = [{ price: 100, number: 2 }, { price: 100, number: 2 }, { price: 100, number: 2 }, { price: 100, number: 2 }].reduce((a, b) => { return a.price * a.number + b.price * b.number; }) console.log(k) // k = NaN
// 算價格 let k = [{ price: 100, number: 2 }, { price: 100, number: 2 }, { price: 100, number: 2 }, { price: 100, number: 2 }].reduce((a, b) => { return a + b.price * b.number; }, 0) console.log(k) // k = 800
3)合並數據
這里的是將key和value和為一個對象,遍歷key數組,給一個初始值{},往初始值{}賦值。這樣做到了合並數據
// 合並多個數據 let keys = ['name', 'age']; let value = ['王少', '20']; let obj = keys.reduce((a, b, index, curent) => { a[b] = value[index]; return a; }, {}); console.log(obj); // obj = {name:'王少',age:'20'}
4)redux的compose方法
首先先創造3個方法,第一個將倆個字符串拼起來,第二給轉化大寫,第三個前后加***
function sum(a, b) { return a + b; } function toUpper(str) { return str.toUpperCase(); } function add(str) { return `***${str}***` }
正常執行三個方法是這樣的,一層套着一層,我們現在有倆種方法實現優化,一種是用reduceRight,從右向左邊執行。 一種是reduce,從左到右執行。
sum(toUpper(add('111','asd')))
我們先來看reduceRight方法,我們先來看看執行的代碼 compose(add,toUpper,sum)('wangshao','666') 這里是執行倆個函數 , 是一個函數套着一個函數 。外層接受函數數組fns,內層接受實參 ‘wangsaho’,‘666’ 。用pop方法取函數數組最后一位賦值給lastFn,lastFn 作為reducRight的初始值
然后開始從右向左遍歷。此時的a是初始值,也就是最后一位add函數。b是第二位 ,toUpper函數。所以返回值就是b(a)。下一次a的值即為 toUpper(add()) ,b的值為sum()。這樣就實現完成了。
function compose(...fns) { return function (...args) { let lastFn = fns.pop(); return fns.reduceRight((a, b) => { return b(a) }, lastFn(...args)) } };
let r = compose(add, toUpper, sum)('wangshao', '666');
然后,我們將上面的函數用箭頭函數優化一下,箭頭函數可以將return和{}去掉。就簡化為下面的代碼。效果是一樣的。
let compose = (...fns) => (...args) => { let lastFn = fns.pop(); return fns.reduceRight((a, b) => b(a), lastFn(...args)) };
最后我們來看看redcue方法,這個我們往淺的思考,就是第一個函數嵌套第二個函數。將結果再次嵌套第三個函數。首先最外層還是接受函數數組fns,遍歷這個數組將結果返回,這里的a,b是和上面一樣的。我們返回一個函數,這個函數是內層函數,參數有實參。 第二次a的值即為toUpper(add(..args)) 。
function compose(...fns) { return fns.reduce((a, b) => { return (...args) => { return a(b(...args)) } }) }
將上面的函數用箭頭函數優化一下,這個更加簡短了
let compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)));
我們看到的這倆個方法,第一個是一年前redux的compose方法,第二個是現在redux的compose方法
然后我們繼續手寫reduce方法,這里在數組的原型上添加一個方法reduce 。 接受倆個參數,一個是回調,一個是初始值prev。然后開始遍歷,次數是數組的長度,這里我們在原型上可以用this指向調用自身的數組。 首先我們做一個判斷,判斷初始值是否為空,是的話則callback的參數a為第一位,b為第二位,index = i+1,第四個參數則為數組本身。將返回的值賦給prev,並且讓i++; 執行第二次函數,第二次走下面,callback的a是上一次放回的結果prev,b是現在的元素,index是i,第四個參數是數組本身不變。總的結果放回prev。可以看到,我這里想用forEach來實現。但是好像是改變不了遍歷次數和索引的。所以最原生還是用for循環吧。
Array.prototype.reduce = function (callback, prev) { for (let i = 0; i < this.length; i++) { if (prev == undefined) { prev = callback(this[i],this[i+1], i + 1, this); i++; } else { prev = callback(prev,this[i], i , this); } } // this.forEach((e, i) => { // if (prev == undefined) { // console.log(e,e+1) // prev = callback(e,e+1, i + 1, this); // } else { // prev = callback(prev,e+1 , i+1, this); // } // }) return prev; }; let r = [1, 2, 3].reduce((a, b, index, current) => { return a + b })