实战:从 Redux 中的代码片段中应用柯里化!

简介: 柯里化(Currying):是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数、返回最终结果的新函数的技术。

image.png

本文通译自:JavaScript Currying: A Practical Example


柯里化(Currying):是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数、返回最终结果的新函数的技术。


想必大家已经不算陌生!


例🌰:

const add = (a,b,c) => {
   return a+b+c
}
add(1,2,3) // 6
const addCurry = a => b => c => a+b+c
addCurry(1)(2)(3) // 6


addCurry 的 return 版本写法:

const addCurryReturn = (a) => { 
  return (b)=> { 
    return (c)=> { 
      return a+b+c 
    } 
  } 
}


OK,有了基本的认知后,直接上实战:柯里化 && Redux

以下代码从 Redux 中摘录:


// Partial file
...
extraReducers: {
  [signup.pending.toString()]: (state, action) => {
    state.loading = true
    state.error = false
    state.fulfilled = false
  },
  [signup.fulfilled.toString()]: (state, action) => {
    state.loading = false
    state.error = false
    state.fulfilled = true
  },
  [signup.rejected.toString()]: (state, action) => {
    state.loading = false
    state.error = true
    state.fulfilled = false
  },
  [signin.pending.toString()]: (state, action) => {
    state.loading = true
    state.error = false
    state.fulfilled = false
  },
  [signin.fulfilled.toString()]: (state, action) => {
    state.loading = false
    state.error = false
    state.fulfilled = true
  },
  [signin.rejected.toString()]: (state, action) => {
    state.loading = false
    state.error = true
    state.fulfilled = false
  },
}
...


从感官上看,这样的写法 —— 太重复冗余!

state.loading = true
    state.error = false
    state.fulfilled = false


对于 state 的设置必须抽象;


我们可以创建一个函数,将 fulfilledloadingerror 设为可配置项,默认值为 false

const setStatus = (state, action) => ({fulfilled = false,loading = false,error = false}) => {
  state.fulfilled = fulfilled
  state.loading = loading
  state.error = error}
setStatus(state)({fulfilled:true}) 


然后,代码就优化成了这样:


extraReducers: {
  [signup.fulfilled.toString()]: (state, action) =>
    setStatus(state)({ fulfilled: true }),
  [signup.pending.toString()]: (state, action) =>
    setStatus(state)({ loading: true }),
  [signup.rejected.toString()]: (state, action) =>
    setStatus(state)({ error: true }),
  [signin.fulfilled.toString()]: (state, action) =>
    setStatus(state)({ fulfilled: true }),
  [signin.pending.toString()]: (state, action) =>
    setStatus(state)({ loading: true }),
  [signin.rejected.toString()]: (state, action) =>
    setStatus(state)({ error: true }),
}


没有设置为 true 的项,都默认为 fasle

还没完,(state, action) =>setStatus(state) 这一部分仍是重复冗余的,必须接着抽象;


我们将 setStatus 这样的写法:

const setStatus = (state) => ({fulfilled = false,loading = false,error = false}) =>  {
  state.fulfilled = fulfilled
  state.loading = loading
  state.error = error}
// 调用
setStatus(state)({fulfilled:true})


改造成以下写法:(敲重点, 柯里化就在此处体现✨)

// 更改传参顺序
const setStatus = ({fulfilled = false,loading = false,error = false}) => (state) => {
  state.fulfilled = fulfilled
  state.loading = loading
  state.error = error}
// 调用
setStatus({fulfilled:true})(state)


最终的改造结果


1. 改造前

extraReducers: {
  [signup.pending.toString()]: (state, action) => {
    state.loading = true
    state.error = false
    state.fulfilled = false
  },
  [signup.fulfilled.toString()]: (state, action) => {
    state.loading = false
    state.error = false
    state.fulfilled = true
  },
  [signup.rejected.toString()]: (state, action) => {
    state.loading = false
    state.error = true
    state.fulfilled = false
  },
  [signin.pending.toString()]: (state, action) => {
    state.loading = true
    state.error = false
    state.fulfilled = false
  },
  [signin.fulfilled.toString()]: (state, action) => {
    state.loading = false
    state.error = false
    state.fulfilled = true
  },
  [signin.rejected.toString()]: (state, action) => {
    state.loading = false
    state.error = true
    state.fulfilled = false
  },
}


2. 改造后

const setStatus = ({
    fulfilled = false,
    loading = false,
    error = false,
  }) =>
  (state, action) => {
    state.fulfilled = fulfilled
    state.loading = loading
    state.error = error
  }
extraReducers: {    
  [signup.pending.toString()]: setStatus({ loading: true }),
  [signup.fulfilled.toString()]: setStatus({ fulfilled: true }),
  [signup.rejected.toString()]: setStatus({ error: true }),
  [signin.pending.toString()]: setStatus({ loading: true }),
  [signin.fulfilled.toString()]: setStatus({ fulfilled: true }),
  [signin.rejected.toString()]: setStatus({ error: true }),
}


Why?


为什么改变了一个传参顺序,就能做到这样的简化效果?

噢,原来最根本的原因是以下的两种写法是等价的!(大道至简


// 写法 1
onClick((state)=> updateState(state))
// 写法 2
onClick(updateState)


其实,函数作为一等公民的思想 —— 即把函数当成一个值来进行传递,太开放(相对于OOP)!

比如:

const add = (a,b) => a+b
const sub = (a,b) => a-b
const calc = (a, b, cb) => cb(a,b)
calc(3,4, add) // 7
calc(3,4, sub) // -1


calc 是高阶函数(接受一个或多个函数作为输入)!

function getName(name) {
  return function greet(){
    console.log('Hello, ', name)
  }
}
const greet = getName('Karthick')
greet() // Hello,  Karthick


getName 也是高阶函数(输出一个函数),返回的是后续再调用的一个函数;

我敲!上面这段代码怎么有点眼熟,有点像我们之前在(《你觉得“惰性求值”在 JS 中会怎么实现?》)讲的 【惰性求值】 ?!


function * st1(){
    setTimeout(()=>{
        console.log("惰性求值")
    },1000)
    yield("后续再调用")
}
let aThunk=st1()
console.log(aThunk) // st1 {<suspended>}
aThunk.next() // {value: '后续再调用', done: false}


确实,闭包结构赋值的时候也不会计算,等到后续调用的时候才计算,就是惰性的呀~

新理解: 在 JavaScript 中,除了 Generator 可以实现惰性求值,闭包也可以呀!Thunk 就是一个闭包!


不是说柯里化吗?咋说到闭包了?


再看一例🌰:

const addCurryReturn = (a) => { 
  return (b)=> { 
    return (c)=> { 
      return a+b+c 
    } 
  } 
}
const add5 = addCurryReturn(5)
console.log(add5) // (b)=> { return (c)=> { return a+b+c } }
const add12 = add5(7)
console.log(add12) // (c)=> { return a+b+c }
add12(7) // 19


当我们调用 add12(7) 的时候,为什么会知道 x = 5y = 7,是因为闭包记住了先前执行中传递的值,这就是二者的关联。


以上,后面再遇见类似的代码结构知道怎么优化了吧!

撰文不易,点赞鼓励 👍👍👍👍👍👍

我是掘金安东尼,公众号同名,输出暴露输入,技术洞见生活,再会~


相关文章
|
3天前
|
Python
Python学习 -- 高阶、闭包、回调、偏函数与装饰器探究
Python学习 -- 高阶、闭包、回调、偏函数与装饰器探究
22 0
|
3天前
|
前端开发 JavaScript
react中的函数柯里化
react中的函数柯里化
25 0
|
3天前
|
自然语言处理 前端开发 JavaScript
说说你对 React Hook的闭包陷阱的理解,有哪些解决方案?
说说你对 React Hook的闭包陷阱的理解,有哪些解决方案?
52 0
|
6月前
vant-函数式组件用法
vant-函数式组件用法
54 0
|
9月前
|
存储 前端开发 开发者
对 React Hook的闭包陷阱的理解,有哪些解决方案?
对 React Hook的闭包陷阱的理解,有哪些解决方案?
80 0
|
前端开发
react hooks 闭包陷阱
react hooks 闭包陷阱
|
存储 前端开发
React中的闭包陷阱以及如何使用useState姿势(I)
React中的闭包陷阱以及如何使用useState姿势
919 0
React中的闭包陷阱以及如何使用useState姿势(I)
|
前端开发 JavaScript
react中高阶函数与高阶组件的运用—(上)(案例详细解释高阶函数)
学习react的同学多多少少对高阶组件有所耳闻,想要学好高阶组件就必须先学好高阶函数的运行,本篇文章就先从高阶函数着手,带大家学习高阶函数和高阶组件的运用
|
JavaScript 前端开发 算法
JS查漏补缺——高阶函数、闭包
JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来理解一下面试题常考的高阶函数、闭包
83 0
react18-学习笔记10-函数和类型推断
react18-学习笔记10-函数和类型推断
55 0