1. 程式人生 > >手寫redux方法以及陣列reduce方法

手寫redux方法以及陣列reduce方法

 

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
})

 

 

有什麼覺得奇怪的地方,互相討論,學習。謝