7种高级函数可以帮助你改善生活

JavaScript的基本构建块之一是函数。在学习JavaScript时,这足以激发任何人对函数语义的兴趣。

但功能不止于此耐人寻味。

你问,“喜欢什么”?

它们可以成为更高阶的函数 - 将其他函数作为参数的函数,或者将函数作为其输出返回的函数。

这一行有很多函数,JavaScript也不例外。在JavaScript中,函数无处不在。你应该为他们感到幸福!

以下7种高阶函数可以改善您的生活:

1.他们增强您现有的代码,增加信心

想象一下,一个函数的任务是从API中检索青蛙列表,使用返回的列表来计算青蛙的平均舌头宽度并返回结果。

例子:

import axios from 'axios'
const getFrogs = async (params) => {
  try {
    const response = await axios.get(
      'https://frogs-and-their-tongues.com',
      params,
    )
    const frogs = response.data.result
    return frogs
  } catch (error) {
    throw error
  }
}
const calcAverageWidthOfTongues = async (params) => {
  try {
    const frogs = await getFrogs(params)
    const tongueWidths = frogs.reduce((sum, frog) => {
      return sum + frog.tongueWidth
    }, 0)
    const averageWidth = tongueWidths / frogs.length
    return averageWidth
  } catch (error) {
    throw error
  }
}
calcAverageWidthOfTongues({
  username: 'bob',
  password: 'the_builder100',
})
  .then((result) => {
    console.log(result)
  })
  .catch((error) => {
    console.error(error)
  })

就像现在一样,我们仅限于返回一个数字。但是如果有一种方法可以将其转换为对象而不改变原始函数呢?

这里的增强功能是能够传递其他选项来自定义和转换输出以获得更好的控制:

import axios from 'axios'

const getFrogs = async (params) => {
  try {
    const frogs = await axios.get('https://frogs-and-their-tongues.com', params)
    return data
  } catch (error) {
    throw error
  }
}

const calcAverageWidthOfTongues = async (params) => {
  try {
    const frogs = await getFrogs(params)
    const tongueWidths = frogs.reduce((sum, frog) => {
      return sum + frog.tongueWidth
    }, 0)
    const averageWidth = tongueWidths / frogs.length
    return averageWidth
  } catch (error) {
    throw error
  }
}

const useTongueObj = (fn, options) => {
  return async (params) => {
    const newParams = { ...params }
    if (options.limit !== undefined) {
      newParams.limit = options.limit
    }
    let averageWidth = await fn(newParams)
    if (typeof options.multiplyBy === 'number') {
      averageWidth = averageWidth * options.multiplyBy
    }
    return {
      averageWidth,
      multiplied: typeof options.multiplyBy === 'number',
      size: averageWidth < 2 ? 'small' : 'large', // size in inches
    }
  }
}

const calcTongueWidths = useTongueObj(calcAverageWidthOfTongues, {
  multiplyBy: 2,
})

calcTongueWidths({ limit: 10 })
  .then((tongueObj) => {
    console.log(tongueObj)
    /*
      result:
        {
          averageWidth: 8,
          multiplied: true,
          size: 'large'
        }
    */
  })
  .catch((error) => {
    console.log(result)
  })

他们节省了宝贵的时间

我想更加强调这一点,让我举一个现实生活中的例子。

高阶函数的最大好处之一是,如果使用正确,它将为周围的人节省大量时间。

在我的工作中,我们使用react-toastify来显示通知。我们每个地方都用它。此外,他们还会在最后一分钟快速做出用户体验决策::“我们应该如何处理这个错误?只需显示toast通知!“完成。

但是,我们开始注意到,当应用程序变得越来越大并且复杂程度越来越高时,我们的Toast通知变得过于频繁。这意味着一些吐司通知在屏幕上多次显示,即使它们上面的吐司完全相同

因此,我们最终利用库提供的API来帮助使用toast.dismiss()通过id删除活动的Toast通知。

为了解释前面的部分,在继续之前显示我们正在导入toast的文件可能是个好主意:

https://gist.github.com/0248e0a376b4239b8c11c7f9ce5f6098

我知道这看起来不太吸引人,但我保证两分钟后会好起来的。

这是我们在单独的组件中检查先前的toast是否已经在屏幕上的内容。如果有,它将尝试删除该吐司并重新展示新的吐司。

import { toast } from 'react-toastify'
import {
  info as toastInfo,
  success as toastSuccess,
  toastIds,
} from 'util/toast'
import App from './App'

const Root = () => {
  const onOnline = () => {
    if (toast.isActive(toastIds.internetOffline)) {
      toast.dismiss(toastIds.internetOffline)
    }
    
    if (toast.isActive(toastIds.retryInternet)) {
      toast.dismiss(toastIds.retryInternet)
    }
    if (!toast.isActive(toastIds.internetOnline)) {
      toastSuccess('You are now reconnected to the internet.', {
        position: 'bottom-center',
        toastId: toastIds.internetOnline,
      })
    }
  }

  const onOffline = () => {
    if (!toast.isActive(toastIds.internetOffline)) {
      toastInfo('You are disconnected from the internet right now.', {
        position: 'bottom-center',
        autoClose: false,
        toastId: toastIds.internetOffline,
      })
    }
  }
  
  useInternet({ onOnline, onOffline })
  
  return <App />
}

这工作正常 - 但是,我们在整个应用程序中有其他需要以相同的方式进行修改。我们必须浏览每个显示toast nofication的文件以删除重复项。

当我们考虑在2019年浏览每个文件时,我们立即知道它不是解决方案。所以我们查看了util/toast.js文件并重构了它来解决我们的问题。以下是它之后的样子:

src/util/toast.js

import React, { isValidElement } from 'react'
import isString from 'lodash/isString'
import isFunction from 'lodash/isFunction'
import { GoCheck, GoAlert } from 'react-icons/go'
import { FaInfoCircle } from 'react-icons/fa'
import { MdPriorityHigh } from 'react-icons/md'
import { toast } from 'react-toastify'

/*
  Calling these toasts most likely happens in the UI 100% of the time.
  So it is safe to render components/elements as toasts.
*/

// Keeping all the toast ids used throughout the app here so we can easily manage/update over time
// This used to show only one toast at a time so the user doesn't get spammed with toast popups
export const toastIds = {
  // APP
  internetOnline: 'internet-online',
  internetOffline: 'internet-offline',
  retryInternet: 'internet-retry',
}

// Note: this toast && is a conditional escape hatch for unit testing to avoid an error.
const getDefaultOptions = (options) => ({
  position: toast && toast.POSITION.BOTTOM_RIGHT,
  ...options,
})

const Toast = ({ children, success, error, info, warning }) => {
  let componentChildren
  // Sometimes we are having an "object is not valid as a react child" error and children magically becomes an API error response, so we must use this fallback string
  if (!isValidElement(children) && !isString(children)) {
    componentChildren = 'An error occurred'
  } else {
    componentChildren = children
  }
  let Icon = GoAlert
  if (success) Icon = GoCheck
  if (error) Icon = GoAlert
  if (info) Icon = FaInfoCircle
  if (warning) Icon = MdPriorityHigh
  return (
    <div style={{ paddingLeft: 10, display: 'flex', alignItems: 'center' }}>
      <div style={{ width: 30, height: 30 }}>
        <Icon style={{ color: '#fff', width: 30, height: 30 }} />
      </div>
      <div style={{ padding: 8, display: 'flex', alignItems: 'center' }}>
        &nbsp;&nbsp;
        <span style={{ color: '#fff' }}>{componentChildren}</span>
      </div>
    </div>
  )
}

const toaster = (function() {
  // Attempt to remove a duplicate toast if it is on the screen
  const ensurePreviousToastIsRemoved = (toastId) => {
    if (toastId) {
      if (toast.isActive(toastId)) {
        toast.dismiss(toastId)
      }
    }
  }
  // Try to get the toast id if provided from options
  const attemptGetToastId = (msg, opts) => {
    let toastId
    if (opts && isString(opts.toastId)) {
      toastId = opts.toastId
    } else if (isString(msg)) {
      // We'll just make the string the id by default if its a string
      toastId = msg
    }
    return toastId
  }
  const handleToast = (type) => (msg, opts) => {
    const toastFn = toast[type]
    if (isFunction(toastFn)) {
      const toastProps = {}
      let className = ''
      const additionalOptions = {}
      const toastId = attemptGetToastId(msg, opts)
      if (toastId) additionalOptions.toastId = toastId
      // Makes sure that the previous toast is removed by using the id, if its still on the screen
      ensurePreviousToastIsRemoved(toastId)
      // Apply the type of toast and its props
      switch (type) {
        case 'success':
          toastProps.success = true
          className = 'toast-success'
          break
        case 'error':
          toastProps.error = true
          className = 'toast-error'
          break
        case 'info':
          toastProps.info = true
          className = 'toast-info'
          break
        case 'warn':
          toastProps.warning = true
          className - 'toast-warn'
          break
        case 'neutral':
          toastProps.warning = true
          className - 'toast-default'
          break
        default:
          className = 'toast-default'
          break
      }
      toastFn(<Toast {...toastProps}>{msg}</Toast>, {
        className,
        ...getDefaultOptions(),
        ...opts,
        ...additionalOptions,
      })
    }
  }
  return {
    success: handleToast('success'),
    error: handleToast('error'),
    info: handleToast('info'),
    warn: handleToast('warn'),
    neutral: handleToast('neutral'),
  }
})()

export const success = toaster.success
export const error = toaster.error
export const info = toaster.info
export const warn = toaster.warn
export const neutral = toaster.neutral

最简单的解决方案是创建更高级函数,而不必遍历每个文件。这样做可以让我们“反转”这些角色,这样我们就不会搜索文件了,而是转向我们的高级函数

这样,文件中的代码不会被修改或触摸。它们仍然正常运行,我们获得了删除重复toast的能力,而无需在最后编写不必要的代码。这节省了时间

3.他们赋予创造“私人世界”的能力

image.png

“私人世界”是什么意思?

好吧,考虑这个例子:

const preparePeopleWithFavoriteColor = (color) => {
  const _people = []
  
  return {
    getPeople() {
      return _people
    },
    addPeople(people) {
      _people.push(...people)
    },
    addPerson(person) {
      _people.push(person)
    },
    gather(people) {
      if (Array.isArray(people)) {
        people.forEach((person) => {
          if (color === person.favoriteColor) {
            _people.push(person)
          }
        })
      }
    },
  }
}

const peopleWhoLoveRed = preparePeopleWithFavoriteColor('red')

axios
  .get('https://someapi.com/peoples')
  .then((response) => {
    const people = response.data.result
    if (people.length) {
      peopleWhoLoveRed.gather(people)
    }
    return axios
      .get('https://someapi.com/other-peoples')
      .then((response) => {
        const morePeople = response.data.result
        if (morePeople.length) {
          everyoneWhoLovesRed.gather(morePeople)
        }
        return
      })
      .then(() => {
        // finally, add me last because i love red too
        peopleWhoLoveRed.addPerson({
          nickName: 'jsmanifest',
          favoriteColor: 'red',
        })
        return axios.post('https://api.redlovers.com/v1/subscribers/', {
          people: peopleWhoLoveRed.getPeople(),
        })
      })
  })
  .catch((error) => {
    console.error(error)
  })

在代码片段中,preparePeopleWithFavoriteColor在调用之前在其块内创建一个私有世界,然后将下一个函数返回给调用者。这个“私有世界”不会通过冲突的名称来污染外部范围,并保留自己的私有变量和值。

此外,它还创建了自己的界面和本地API来管理其人员列表。外部的范围永远不会知道内部发生了什么 - 并且他们这样做的唯一方法取决于它返回的公共方法。

如果你足够偷偷摸摸,你可以在这些可爱的小块中偷偷进行秘密的API调用,向你发送每次代码运行时爱红色的人的列表 - 用户甚至无法分辨,因为它没有影响应用中的任何其他内容。

它们可以作为快速而简单的解决方案,暂时降低压力

在最黑暗的时代,更高阶的函数通常可以挽救您的时间,因为它可以是解决代码问题的最快捷方式,而不会对您的应用程序进行任何明显的更改。

我曾经遇到过一个问题,即使用户在我们的应用程序中退出,用户仍然会在导航栏中看到“Dashboard”按钮。如果应用程序检测到他们已经登录,则应该只显示仪表板按钮。当他们点击仪表板按钮时,他们会被重定向到他们的用户仪表板页面。

当时,他们退出的方式是转到他们的个人资料页面并点击退出按钮。单击该按钮时,它们应该已完全注销并重定向回登录页面。

为什么是在仪表板按钮仍然在导航栏中显示?用户注销了,当我调试问题时,客户端中包括本地存储的所有内容都被清除了。

然后我意识到问题是redux状态的某些部分仍然存在。该重定向是使用导航即进行了优化,用户在不同的路由重定向,而不用刷新整个页面。这导致了缓存问题。

那么我如何确保在用户注销后redux中的整个状态重置?

注销后,我们让redux调度一个动作类型为LOGOUT的动作创建者,该动作类型应该向应用程序发出用户正在注销的信号。

我立即专注于提供解决方案是以某种方式修改动作创建者。它似乎是提供这种增强的最佳位置。

然后我意识到这不是最好的解决方案,因为有一个更好的解决方案:使用更高阶的功能(但不是这里)。

这里有一个很好的例子,说明为什么高级函数在JavaScript 中如此强大

我们的root reducer文件src/reducers/index.js导出了这个:


export default combineReducers({
  app,
  form: formReducer,
})

然后,我们应用更高级的函数来包装它,并在检测到调度类型为LOGOUT的动作时应用重置状态逻辑:

const appReducer = combineReducers({
  app,
  form: formReducer,
})

// Higher order reducer that resets the redux state when we dispatch a logout action
const rootReducer = (state, action) => {
  if (['LOGOUT'].includes(action.type)) {
    state = undefined
  }
  return appReducer(state, action)
}

export default rootReducer

这会将状态重置回其初始状态,因为我们将状态重新分配给undefinedRedux将使用未定义状态调用reducer并返回应用程序的初始状态

5.可以在任何地方保持,操作和传递操纵数据,为您提供轻松的时间测试多种解决方案

您可以使用更高级函数执行的最酷的事情之一是保留数据的私有缓存,操纵它并随意将其传递到应用程序中的任何位置。它们都不会被外部篡改。

例如,如果要查找存储访问令牌的位置,以便当令牌在用户会话中间的30分钟内到期时,您可以刷新该令牌并重新设置以供进一步使用:

const Api = function(params) {
  const _store_ = {
    accessToken: null,
  }
  
  return {
    getAccessToken() {
      return _store.accessToken
    },
    async login() {
      try {
        const response = await axios.post(
          'https://something.com/v1/auth',
          params,
        )
        return response.data
      } catch (error) {
        throw error
      }
    },
    async refreshToken() {
      try {
        const response = await axios.post(
          'https://something.com/v1/access_token/',
          params,
        )
        const { token } = response.data
        _store.accessToken = token
        return token
      } catch (error) {
        throw error
      }
    },
    setAccessToken(token) {
      if (token === undefined) {
        throw new Error('token is undefined')
      }
      _store.accessToken = token
    },
    // ...other methods
  }
}

const api = Api({
  username: 'bob',
  password: 'the_builder123',
})

api
  .refreshToken())
  .then((token) => console.log(token))
  .catch(console.error)

6.它赋予你创建新东西的能力

让我们说你决定创建一个RPG游戏交给你的4个老侄子,试图阻止他每天烦扰你。在这个游戏中你决定要创建一批战士:

const characters = []

const Warrior = function createWarrior(name) {
  this.name = name
  this.hp = 100
  this.mp = 100
  this.defence = 60
  
  return {
    // Slash the enemy, decreasing their hp down by 35
    slash(target) {
      target.hp -= 35
      return target
    },
    // Increases the warrior's defence by 100 for 40 seconds.
    // Each second will decrement their defence by 1 as the time runs out.
    battleCry() {
      this.defence += 100
      this.battleCryInterval = setInterval(() => {
        this.defence -= 1
      }, 1000)
      this.battleCryTimeout = setTimeout(() => {
        this.defence = 60
      }, 40000)
    },
  }
}

您可以创建一个更高级的函数,首先接受一个名称列表来创建战士,然后返回一个有助于创建战士的新函数:

const massCreateWarriors = function(names) {
  return (onCreate) => {
    const warriors = []
    
    names.forEach((name) => {
      const newWarrior = new Warrior(name)
      if (onCreate) onCreate(newWarrior)
      warriors.push(newWarrior)
    })
    
    return warriors
  }
}

const prepareWarriors = massCreateWarriors(['bob', 'joe', 'sally', 'woodie'])

const newWarriors = prepareWarriors(function onCreate(newWarrior) {
  if (newWarrior.name === 'sally') {
    newWarrior.theme = 'red'
  }
  characters.push(newWarrior)
})

创建一个辅助函数来为你创建大量字符不是很好,而不是每次都为不同的字符硬编码吗?

7.它可以帮助您与老板和同事建立健康的关系

高级函数可以帮助解决许多问题。有了这个说法,高级函数可以带来很多有用的好处,比如减小代码大小和重新使用,你就会减少用这种方式对周围人施加压力的可能性。你的同事会喜欢你不仅仅是一个开发人员,而是一个努力做伟大事业的开发人员。此外,您还可以增加新开发人员学习代码以帮助他们提高技能的可能性。这使您对团队,老板和整个公司都非常有价值。

...... 当你的同事开心时,他们很可能会带上免费的甜甜圈

结论

这就结束了帖子的结尾!我希望你喜欢它,并留意未来我的更多帖子!

转:https://codeburst.io/here-are-7-ways-higher-order-functions-can-improve-your-life-a392aa6b29d2

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容