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' }}>
<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.他们赋予创造“私人世界”的能力
“私人世界”是什么意思?
好吧,考虑这个例子:
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
这会将状态重置回其初始状态,因为我们将状态重新分配给undefined。Redux将使用未定义状态调用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