惰性调用
  
业务场景中,有些地方非常适合函数的惰性调用。利用惰性调用来优雅的实现代码的解耦合,同时也能快速的完成业务,准备下班。
介绍
所谓惰性调用也可以说是分步函数或者分段函数,从字面意思可以看出就是将一个功能点分两个或多个步骤完成。举个简单的分段函数例子:
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日