惰性调用
业务场景中,有些地方非常适合函数的惰性调用。利用惰性调用来优雅的实现代码的解耦合,同时也能快速的完成业务,准备下班。
介绍
所谓惰性调用也可以说是分步函数或者分段函数,从字面意思可以看出就是将一个功能点分两个或多个步骤完成。举个简单的分段函数例子:
1
| const foo = a => b => a + b
|
从这个例子能看出来,所谓分段函数就是我们通俗的讲的闭包,所谓闭包简单的讲就是函数内返回一个函数或者调用一个内部函数,在这个闭包作用域内会将变量的值锁住,即闭包内层函数可以获取到闭包外层函数的局部变量。这个理论是我根据自己的理解瞎讲的,自认为有点说法。
实践一下
1 2 3 4
| const foo = a => => a + b
const bar = foo(1) console.log(bar(1))
|
可以看到,这个分段函数被分成了两步执行,执行第二步的时候也能获取到第一段传进来的值。这个就是简单的分段函数。
应用场景
扯了那么多没用,实践大于理论。来个demo看看。

比如上面这个例子,点击列表项的选择按钮,在弹出模态框中选择合适项填充到列表项中。像这样的例子在erp系统中是非常常见的,如何使用函数惰性调用的方式来优雅的完成业务?看接下来的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <template> <div class="list"> <div class="item" v-for="(item, index) in arrs" :key="index"> <span>{{ index + 1 }}</span> <span class="name">{{ item.name || 'unset' }}</span> <span class="cat">{{ item.category || 'unset' }}</span> <span class="xxx">{{ item.xxx || 'unset' }}</span> <button @click="showModal(item)">choose</button> </div> </div> <XXXSelectModal ref="modalRef" @onGet="handleGet"/> </template> <script setup> const arrs = ref([......]) let callback = null const modalRef = ref(null) const showModal = item => { callback = target => { Object.keys(item).forEach(key => { if (!target[key]) return item[key] = target[key] }) } modalRef.value.show() } const handleGet = (slectedValue) => { callback && callback(slectedValue) callback = null } </script>
|
可以看到通过函数的分段执行,可以很轻松的实现业务的调用,这只是一个最简单的例子,在实际业务中往往会有更复杂更抽象的业务。掌握这个简单的操作可以省心很多。
🔺难度UP
「作为一个喜欢装逼的bugger,多少得写点让人觉得很屌的东西来展示下自己。」
函数分段执行也就是控制一个复杂函数的粒度,对于复杂函数的分段惰性操作,我们可以称之为函数柯理化。来点模拟业务处理的demo:
业务1:从这个数据中提取出所有name和age集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const data = [{ name: 'Bent', age: 18 }, { name: 'Jack', age: 22 }, { name: 'Mike', age: 33 }]
const getField = field => obj => obj[field]
const nameArr = data.map(getField('name')) const ageArr = data.map(getField('age'))
|
当然你可以直接…
1 2
| const ageArr = data.map(el => el.age) const nameArr = data.map(el => el.name)
|
业务2:根据不用场景需求来给对象数组按照字段排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const data = [{ name: 'Bent', age: 18, balance: 2284 }, { name: 'Jack', age: 22, balance: 1232 }, { name: 'Mike', age: 33, balance: 4523 }]
const execSort = (field, mode = 'asc') => { return (a, b) => { if (mode == 'desc') return b[field] - a[field] if (mode == 'asc') return a[field] - b[field] return 0 } }
data.sort(execSort('age'))
data.sort(execSort('age', 'desc'))
|
当然你可以直接…
1 2 3 4 5 6 7
| data.sort((a, b) => b.age - a.age)
data.sort((a, b) => a.age - b.age)
data.sort((a, b) => b.balance - a.balance)
|
业务3:在每次网络请求前开启加载效果,请求结束后关闭加载效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
export const withLoading = (fn) => { return (...args) => { try { showLoading() const res = fn(...args) if (res instanceof Promise) { return res.then(resFix => { hideLoading() return resFix }).catch(e => { hideLoading() throw e }) } } catch (e) { hideLoading() throw e } } }
|
在业务中引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { withLoading } from '@/utils/loading'
const getDataWithLoading = async () => { const res = await withLoading($axios.blog.getBlogById)(1) }
const getData = async () => { const res = await $axios.blog.getBlogById(1) }
getData() getDataWithLoading()
|
这时候你就会发现,用常规的方式没办法做到这么优雅的解耦,传统的方式你通常会这样实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const getDataWithLoading = async () => { try { showLoading() const res = await $axios.blog.getBlogById(1) hideLoading() return res.data } catch (e) { hideLoading() throw e } }
getData()
|
这是函数柯理化的作用就体现出来了,withLoading
这个函数是一个纯函数,主要的作用就是给异步操作提供加载效果,它不会对作用域造成污染。还不赶紧用起来?
文末;
函数的惰性调用能够让你和其他函数进行很好的组合工作,但是最好做到每个函数的单一职责,避免对作用域造成污染或者减少作用域污染。本文主要提供FP(函数式编程)
的思路,让您在遇到问题的同时有一些巧妙的解决办法……如果你对函数式编程比较感兴趣的话,可以看看我之前写的文章。「文章:函数式编程」在这篇文章里你会看到一些更有意思的东西。
👋 ;
前端, 技术分享 — 2024年4月13日