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

          前端必知道的幾個 JavaScript 高級函數(shù)

          共 2325字,需瀏覽 5分鐘

           ·

          2021-11-13 03:23

          高階函數(shù)是對其他函數(shù)進行操作的函數(shù),可以將它們作為參數(shù)或通過返回它們。簡單來說,高階函數(shù)是一個函數(shù),它接收函數(shù)作為參數(shù)或?qū)⒑瘮?shù)作為輸出返回。

          例如?Array.prototype.mapArray.prototype.filterArray.prototype.reduce?都是一些高階函數(shù)。

          尾調(diào)用和尾遞歸

          尾調(diào)用(Tail Call)是函數(shù)式編程的一個重要概念,本身非常簡單,一句話就能說清楚。就是指某個函數(shù)的最后一步是調(diào)用另一個函數(shù)。

          function g(x) {
          console.log(x)
          }
          function f(x) {
          return g(x)
          }
          console.log(f(1))
          //上面代碼中,函數(shù)f的最后一步是調(diào)用函數(shù)g,這就是尾調(diào)用。

          上面代碼中,函數(shù) f 的最后一步是調(diào)用函數(shù) g,這就是尾調(diào)用。尾調(diào)用不一定出現(xiàn)在函數(shù)尾部,只要是最后一步操作即可。

          函數(shù)調(diào)用自身,稱為遞歸。如果尾調(diào)用自身,就稱為尾遞歸。遞歸非常耗費內(nèi)存,因為需要同時保存成千上百個調(diào)用幀,很容易發(fā)生棧溢出錯誤。但是隊伍尾遞歸來說,由于只存在一個調(diào)用幀,所以永遠不會發(fā)生棧溢出錯誤。

          function factorial(n) {
          if (n === 1) {
          return 1
          }
          return n * factorial(n - 1)
          }

          上面代碼是一個階乘函數(shù),計算 n 的階乘,最多需要保存 n 個調(diào)用數(shù)據(jù),復雜度為 O(n),如果改寫成尾調(diào)用,只保留一個調(diào)用記錄,復雜度為 O(1)。

          function factor(n, total) {
          if (n === 1) {
          return total
          }
          return factor(n - 1, n * total)
          }

          斐波拉切數(shù)列也是可以用于尾調(diào)用。

          function Fibonacci(n) {
          if (n <= 1) {
          return 1
          }
          return Fibonacci(n - 1) + Fibonacci(n - 2)
          }
          //尾遞歸
          function Fibona(n, ac1 = 1, ac2 = 1) {
          if (n <= 1) {
          return ac2
          }
          return Fibona(n - 1, ac2, ac1 + ac2)
          }

          柯理化函數(shù)

          在數(shù)學和計算機科學中,柯里化是一種將使用多個參數(shù)的一個函數(shù)轉(zhuǎn)換成一系列使用一個參數(shù)的函數(shù)的技術。所謂柯里化就是把具有較多參數(shù)的函數(shù)轉(zhuǎn)換成具有較少參數(shù)的函數(shù)的過程。?
          舉個例子

          //普通函數(shù)
          function fn(a, b, c, d, e) {
          console.log(a, b, c, d, e)
          }
          //生成的柯里化函數(shù)
          let _fn = curry(fn)

          _fn(1, 2, 3, 4, 5) // print: 1,2,3,4,5
          _fn(1)(2)(3, 4, 5) // print: 1,2,3,4,5
          _fn(1, 2)(3, 4)(5) // print: 1,2,3,4,5
          _fn(1)(2)(3)(4)(5) // print: 1,2,3,4,5

          柯理化函數(shù)的實現(xiàn)

          // 對求和函數(shù)做curry化
          let f1 = curry(add, 1, 2, 3)
          console.log('復雜版', f1()) // 6

          // 對求和函數(shù)做curry化
          let f2 = curry(add, 1, 2)
          console.log('復雜版', f2(3)) // 6

          // 對求和函數(shù)做curry化
          let f3 = curry(add)
          console.log('復雜版', f3(1, 2, 3)) // 6

          // 復雜版curry函數(shù)可以多次調(diào)用,如下:
          console.log('復雜版', f3(1)(2)(3)) // 6
          console.log('復雜版', f3(1, 2)(3)) // 6
          console.log('復雜版', f3(1)(2, 3)) // 6

          // 復雜版(每次可傳入不定數(shù)量的參數(shù),當所傳參數(shù)總數(shù)不少于函數(shù)的形參總數(shù)時,才會執(zhí)行)
          function curry(fn) {
          // 閉包
          // 緩存除函數(shù)fn之外的所有參數(shù)
          let args = Array.prototype.slice.call(arguments, 1)
          return function() {
          // 連接已緩存的老的參數(shù)和新傳入的參數(shù)(即把每次傳入的參數(shù)全部先保存下來,但是并不執(zhí)行)
          let newArgs = args.concat(Array.from(arguments))
          if (newArgs.length < fn.length) {
          // 累積的參數(shù)總數(shù)少于fn形參總數(shù)
          // 遞歸傳入fn和已累積的參數(shù)
          return curry.call(this, fn, ...newArgs)
          } else {
          // 調(diào)用
          return fn.apply(this, newArgs)
          }
          }
          }

          柯里化的用途

          柯里化實際是把簡答的問題復雜化了,但是復雜化的同時,我們在使用函數(shù)時擁有了更加多的自由度。而這里對于函數(shù)參數(shù)的自由處理,正是柯里化的核心所在。柯里化本質(zhì)上是降低通用性,提高適用性。來看一個例子:

          我們工作中會遇到各種需要通過正則檢驗的需求,比如校驗電話號碼、校驗郵箱、校驗身份證號、校驗密碼等, 這時我們會封裝一個通用函數(shù) checkByRegExp ,接收兩個參數(shù),校驗的正則對象和待校驗的字符串

          function checkByRegExp(regExp, string) {
          return regExp.text(string)
          }

          checkByRegExp(/^1\d{10}$/, '18642838455') // 校驗電話號碼
          checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, '[email protected]') // 校驗郵箱

          我們每次進行校驗的時候都需要輸入一串正則,再校驗同一類型的數(shù)據(jù)時,相同的正則我們需要寫多次, 這就導致我們在使用的時候效率低下,并且由于 checkByRegExp 函數(shù)本身是一個工具函數(shù)并沒有任何意義。此時,我們可以借助柯里化對 checkByRegExp 函數(shù)進行封裝,以簡化代碼書寫,提高代碼可讀性。

          //進行柯里化
          let _check = curry(checkByRegExp)
          //生成工具函數(shù),驗證電話號碼
          let checkCellPhone = _check(/^1\d{10}$/)
          //生成工具函數(shù),驗證郵箱
          let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/)

          checkCellPhone('18642838455') // 校驗電話號碼
          checkCellPhone('13109840560') // 校驗電話號碼
          checkCellPhone('13204061212') // 校驗電話號碼

          checkEmail('[email protected]') // 校驗郵箱
          checkEmail('[email protected]') // 校驗郵箱
          checkEmail('[email protected]') // 校驗郵箱

          柯里化函數(shù)參數(shù) length

          函數(shù) currying 的實現(xiàn)中,使用了 fn.length 來表示函數(shù)參數(shù)的個數(shù),那 fn.length 表示函數(shù)的所有參數(shù)個數(shù)嗎?并不是。

          函數(shù)的 length 屬性獲取的是形參的個數(shù),但是形參的數(shù)量不包括剩余參數(shù)個數(shù),而且僅包括第一個具有默認值之前的參數(shù)個數(shù),看下面的例子。

          ((a, b, c) => {}).length
          // 3

          ((a, b, c = 3) => {}).length
          // 2

          ((a, b = 2, c) => {}).length
          // 1

          ((a = 1, b, c) => {}).length
          // 0

          ((...args) => {}).length
          // 0

          const fn = (...args) => {
          console.log(args.length)
          }
          fn(1, 2, 3)
          // 3

          compose 函數(shù)

          compose 就是組合函數(shù),將子函數(shù)串聯(lián)起來執(zhí)行,一個函數(shù)的輸出結(jié)果是另一個函數(shù)的輸入?yún)?shù),一旦第一個函數(shù)開始執(zhí)行,會像多米諾骨牌一樣推導執(zhí)行后續(xù)函數(shù)。

          const greeting = name => `Hello ${name}`
          const toUpper = str => str.toUpperCase()

          toUpper(greeting('Onion')) // HELLO ONION

          compose 函數(shù)的特點

          • compose 接受函數(shù)作為參數(shù),從右向左執(zhí)行,返回類型函數(shù)

          • fn()全部參數(shù)傳給最右邊的函數(shù),得到結(jié)果后傳給倒數(shù)第二個,依次傳遞

          compose 的實現(xiàn)

          var compose = function(...args) {
          var len = args.length // args函數(shù)的個數(shù)
          var count = len - 1
          var result
          return function func(...args1) {
          // func函數(shù)的args1參數(shù)枚舉
          result = args[count].call(this, args1)
          if (count > 0) {
          count--
          return func.call(null, result) // result 上一個函數(shù)的返回結(jié)果
          } else {
          //回復count初始狀態(tài)
          count = len - 1
          return result
          }
          }
          }

          舉個例子

          var greeting = (name) =>  `Hello ${name}`
          var toUpper = str => str.toUpperCase()
          var fn = compose(toUpper, greeting)
          console.log(fn('jack'))

          大家熟悉的 webpack 里面的 loader 執(zhí)行順序是從右到左,是因為webpack 選擇的是 compose 方式,從右到左依次執(zhí)行 loader,每個 loader 是一個函數(shù)。

          rules: [
          { test: /\.css$/, use: ['style-loader', 'css-loader'] }
          ]

          如上,webpack 使用了 style-loader 和 css-loader,它是先用 css-loader 加載.css 文件,然后 style-loader 將內(nèi)部樣式注入到我們的 html 頁面。

          webpack 里面的 compose 代碼如下:

          const compose = (...fns) => {
          return fns.reduce(
          (prevFn, nextFn) => {
          return value =>prevFn(nextFn(value))
          },
          value => value
          )
          }

          關注

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  翔田千里无码破解HD | 欧美男女日逼视频 | 成人操比| 欧美一级电影在线播放 | 美女乱伦免费 |