<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官直呼內(nèi)行!如何實現(xiàn)一個比較完美的reduce函數(shù)?

          共 12195字,需瀏覽 25分鐘

           ·

          2022-07-11 17:52

          基本用法

          reduce函數(shù)是js原生提供的用于處理數(shù)組結(jié)構(gòu)的函數(shù)。

          先來看一下MDN中的介紹:

          reduce() 方法對數(shù)組中的每個元素按序執(zhí)行一個由用戶提供的 reducer 函數(shù),每一次運(yùn)行 reducer 會將先前元素的計算結(jié)果作為參數(shù)傳入,最后將其結(jié)果匯總為單個返回值。

          參數(shù)

          1. callbackFn 一個 reducer 函數(shù),包含四個參數(shù):
          • previousValue:上一次調(diào)用 callbackFn時的返回值。在第一次調(diào)用時,若指定了初始值 initialValue,其值則為 initialValue,否則為數(shù)組索引為 0 的元素 array[0]。
          • currentValue:數(shù)組中正在處理的元素。在第一次調(diào)用時,若指定了初始值 initialValue,其值則為數(shù)組索引為 0 的元素 array[0],否則為 array[1]
          • currentIndex:數(shù)組中正在處理的元素的索引。若指定了初始值 initialValue,則起始索引號為 0,否則從索引 1 起始。
          • array:用于遍歷的數(shù)組。
          1. initialValue 可選 作為第一次調(diào)用 callback 函數(shù)時參數(shù) previousValue 的值。若指定了初始值 initialValue,則 currentValue 則將使用數(shù)組第一個元素;否則 previousValue 將使用數(shù)組第一個元素,而 currentValue 將使用數(shù)組第二個元素。

          reduce函數(shù)功能是非常強(qiáng)大的適用于非常多的場景:

          比如使用reduce函數(shù)實現(xiàn)累加:

            let total = [ 0123 ].reduce(
              ( previousValue, currentValue ) => previousValue + currentValue,
              0
            )
            // 6

          生成新數(shù)組:

            let total = [ 0123 ].reduce(
              function (pre, cur{
                pre.push(cur + 1)
                return pre
              },
              []
            )
            // [1, 2, 3, 4]

          等等.....

          那么問題來了,如何手寫實現(xiàn)一個reduce函數(shù)呢?

          實現(xiàn)基礎(chǔ)版本

          根據(jù)文檔可知,reduce函數(shù)接受一個運(yùn)行函數(shù)和一個初始的默認(rèn)值

             /**
              *
              * @param {Array} data 原始數(shù)組
              * @param {Function} iteratee 運(yùn)行函數(shù)
              * @param {Any} memo 初始值
              * @returns {boolean} True if value is an FormData, otherwise false
              */

            function myReduce(data, iteratee, memo{
                // ...
            }

          接下來實現(xiàn)基本功能

          reduce函數(shù)的重點就是要將結(jié)果再次傳入執(zhí)行函數(shù)中進(jìn)行處理

            function myReduce(data, iteratee, memo{
             for(let i = 0; i < data.length; i++) {
              memo = iteratee(memo, data[i], i, data)
             }
             return memo
            }

          需求一:增加this綁定

          其實reduce函數(shù)可以指定自定義對象綁定this

          在這里可以使用call對函數(shù)進(jìn)行重新綁定

            function myReduce(data, iteratee, memo, context{
             // 重置iteratee函數(shù)的this指向
             iteratee = bind(iteratee, context)
            
             for(let i = 0; i < data.length; i++) {
              memo = iteratee(memo, data[i], i, data)
             }
             return memo
            }
            
            // 綁定函數(shù) 使用call進(jìn)行綁定
            function bind(fn, context{
              // 返回一個匿名函數(shù),執(zhí)行時重置this指向
              return function(memo, value, index, collection{
                return fn.call(context, memo, value, index, collection);
              };
            }
            

          需求二:增加對第二個參數(shù)默認(rèn)值的支持

          reduce函數(shù)的第三個參數(shù)也是可選值,如果沒有傳遞第三個參數(shù),那么直接使用傳入數(shù)據(jù)的第一個位置初始化

            function myReduce(data, iteratee, memo, context{
             // 重置iteratee函數(shù)的this指向
             iteratee = bind(iteratee, context)
             // 判斷是否傳遞了第三個參數(shù)
             let initial  = arguments.length >= 3// 新增
             // 初始的遍歷下標(biāo)
             let index = 0 // 新增
            
             if(!initial) { // 新增
              // 如果用戶沒有傳入默認(rèn)值,那么就取數(shù)據(jù)的第一項作為默認(rèn)值
              memo = data[index] // 新增
              // 所以遍歷就要從第二項開始
              index += 1 // 新增
             }
            
             for(let i = index; i < data.length; i++) { // 修改
              memo = iteratee(memo, data[i], i, data)
             }
             return memo
            }
            
            // 綁定函數(shù) 使用call進(jìn)行綁定
            function bind(fn, context{
              return function(memo, value, index, collection{
                return fn.call(context, memo, value, index, collection);
              };
            }
            

          需求三:支持對象

          js原生的reduce函數(shù)是不支持對象這種數(shù)據(jù)結(jié)構(gòu)的,那么如何完善我們的reduce函數(shù)呢?

          其實只需要取出對象中所有的key,然后遍歷key就可以了

           function myReduce(data, iteratee, memo, context{
           
             iteratee = bind(iteratee, context)
             // 取出所有的key值
             let _keys = !Array.isArray(data) && Object.keys(data) // 新增
             // 長度賦值
             let len = (_keys || data).length // 新增
           
             let initial  = arguments.length >= 3;
             let index = 0
            
             if(!initial) {
              // 如果沒有設(shè)置默認(rèn)值初始值,那么取第一個值的操作也要區(qū)分對象/數(shù)組
              memo = data[ _keys ? _keys[index] : index] // 修改
              index += 1
             }
            
             for(let i = index; i < len; i++) {
              // 取key值
              let currentKey = _keys ? _keys[i] : i // 新增
              memo = iteratee(memo, data[currentKey], currentKey, data) // 修改
             }
             return memo
            }
            
            function bind(fn, context{
              return function(memo, value, index, collection{
                return fn.call(context, memo, value, index, collection);
              };
            }
            

          需求四:reduceRight

          其實以上的內(nèi)容已經(jīng)是一個比較完整的reduce函數(shù)了,最后一個擴(kuò)展內(nèi)容是reduceRight函數(shù),其實reduceRight函數(shù)的功能也很簡單,就是在遍歷的時候倒序進(jìn)行操作,例如:

           let total = [ 0, 1, 2, 3 ].reduce(
              function (pre, cur) {
                pre.push(cur + 1)
                return pre
              },
              []
            )
            
            // [1, 2, 3, 4]

          其實實現(xiàn)這個功能也非常簡單,只需要初始操作的值更改為最后一個元素的位置就可以了:

          // 加入一個參數(shù)dir,用于標(biāo)識正序/倒序遍歷
          function myReduce(data, iteratee, memo, context, dir//修改

            iteratee = bind(iteratee, context)
            let _keys = !Array.isArray(data) && Object.keys(data)
            let len = (_keys || data).length

            let initial  = arguments.length >= 3;

            // 定義下標(biāo)
            let index = dir > 0 ? 0 : len - 1 // 修改
           
            if(!initial) {
             memo = data[ _keys ? _keys[index] : index]
             // 定義初始值
             index += dir // 修改
            }
            // 每次修改只需步進(jìn)指定的值
            for(;index >= 0 && index < len; index += dir) { // 修改
             let currentKey = _keys ? _keys[index] : index
             memo = iteratee(memo, data[currentKey], currentKey, data)
            }
            return memo
           }
           
           function bind(fn, context{
             if (!context) return fn;
             return function(memo, value, index, collection{
               return fn.call(context, memo, value, index, collection);
             };
           }

          調(diào)用的時候直接傳入最后一個參數(shù)為1 / \-1即可

           myReduce([1234], function(pre, cur{
               console.log(cur)
           }, [], 1)
           
           myReduce([1234], function(pre, cur{
               console.log(cur)
           }, [], -1)

          最后將整個函數(shù)進(jìn)行重構(gòu)抽離成為一個單獨(dú)的函數(shù):

           function createReduce(dir{
               function reduce({
             // ....
               }
           
               return function({
             return reduce()
               }
           }

          最后最終的代碼如下:

           function createReduce(dir{
           
               function reduce(data, fn, memo, initial{
                   let _keys = Array.isArray(data) && Object.keys(data),
                       len = (_keys || data).length,
                       index = dir > 0 ? 0 : len - 1;

                   if (!initial) {
                       memo = data[_keys ? _keys[index] : index]
                       index += dir
                   }

                   for (; index >= 0 && index < len; index += dir) {
                       let currentKey = _keys ? _keys[index] : index
                       memo = fn(memo, data[currentKey], currentKey, data)
                   }
                   return memo
               }


               return function (data, fn, memo, context{
                   let initial = arguments.length >= 3
                   return reduce(data, bind(fn, context), memo, initial)
               }
           }

           function bind(fn, context{
               if (!context) return fn;
               return function (memo, value, index, collection{
                   return fn.call(context, memo, value, index, collection);
               };
           }

           let reduce = createReduce(1)
           let reduceRight = createReduce(-1)

          而這種實現(xiàn)方式也是underscore.js所實現(xiàn)的reduce函數(shù)的方式。

          寫在最后 ?

          未來可能會更新實現(xiàn)mini-vue3javascript基礎(chǔ)知識系列,希望能一直堅持下去,期待多多點贊????,一起進(jìn)步!????


          關(guān)于本文

          來自:pino

          https://juejin.cn/post/7113743909452251167


          最后


          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對你有幫助,在看」是最大的支持
           》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持


          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  高清无码视频在线免费观看 | 国产精品成人网站豆花 | 大屌视频网 | a视频在线免费 | aaa高清在线 |