最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 柯里化理解

    正文概述 掘金(绛ོ珠ོ璃ོ落ོ ོ)   2020-12-08   357

    前言

    要弄清什么是函数柯里化,首先要知道函数式编程; 函数式编程是一种将函数作为参数传递和返回,并且没有副作用的一种编程方式;函数式编程也带来了很多概念,函数柯里化只是函数式编程中的一种模式,归纳的一种概念。

    • 纯函数(Pure Function): 只是返回新的值,不改变系统变量;
    • 柯里化(Currying): 接受多个参数的函数转换成接受单一参数的函数的操作;
    • 高阶函数(Higher-order function): 一个可以接收函数作为参数,甚至返回一个函数的函数

    什么是函数柯里化

    函数柯里化(currying)又称部分求值。 一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

    有人概括说柯里化就是将一个多元函数转换为低元函数的操作

    闭包就是能够读取其他函数内部变量的函数,所以闭包可以理解成为定义在一个函数内部的函数

    案例分析讲解

    这里以一个常用功能的封装做引子,我们知道判断变量类型有四种方法

    1. typeof 只能判断基本数据类型,不能区分对象类型 例如typeof [] , typeof {}返回均是"object"
    2. constructor 可以找到这个变量是通过谁构造出来的 [].constructor ({}).constructor
    3. instanceof 判断谁是谁的实例,proto
    4. Object.prototype.toString.call()

    第4钟方法能对所有的数据类型都能进行判断,即使是 null 和 undefined 。因此我们考虑将其进行封装,以便调用

    封装type类型判断函数

    简单封装

    function isType(type,value) {
      return Object.prototype.toString.call(value) === `[object ${type}]`
    }
    
    isType('Array', []) // true
    isType('Array', {}) // false
    isType('String', 'str') // true
    

    上述简单封装后看起来使用还比较方便,但是有一个问题,就是如果目标类型书写有误,就会到处判断不准确,如何解决?

    解决输入有误导致判断不准确问题

    解决方法一:抛出异常

    const typeArr = ['String','Number','Boolean', 'Array','Symbol','Null', 'Undefined','Function','Object','Arguments']
    function isType(type,value) {
      if (!typeArr.includes(type)) {
        throw(`${type} is not in ${typeArr}`)
      }
      return Object.prototype.toString.call(value) === `[object ${type}]`
    }
    
    

    缺陷:虽然解决了使用时书写错误会引发的判断不准确问题,但问题也很明显,

    • 第一个是使用时还是需要输入多个参数,使用不太友好,多种同类型判断需要多次重复输入相关目标类型;
    • 第二个需要将所有正确的类型进行涵盖,不具有普遍性;

    思路:我们想看能不能更细分,比方直接判断是不是isArray([]),isString('xx'),只用传入需要判断的值就行

    解决方法二:柯里化

    function isType(type) {
      return function(value) {
        return Object.prototype.toString.call(value) === `[object ${type}]`
      }
    }
    // 定义
    const isArray = isType('Array')
    const isString = isString('String')
    // 使用
    console.log(isArray([])) // true
    console.log(isArray('1234')) // false
    console.log(isString('1234')) // true
    console.log(isString(1234)) // false
    

    这样我们在需要经常使用的几个的类型就可以定义出其对应的方法;

    归纳与扩展

    仔细看,柯里化之后的函数和未柯里化的简单的函数的关系

    // 未柯里化的函数
    function isType(type,value) {
      return Object.prototype.toString.call(value) === `[object ${type}]`
    }
    // 未柯里化函数的使用
    isType('Array', []) // true
    
    // 柯里化后的函数
    function isType(type) {
      return function(value) {
        return Object.prototype.toString.call(value) === `[object ${type}]`
      }
    }
    // 柯里化之后的使用
    const isArray = isType('Array')
    console.log(isArray([])) 
    // 等同于
    console.log(isType('Array')([]))
    

    通过比较发现,其实柯里化后将原本一次调用需要传入两个参数('Array', []) 变成了使用时先传入一个参数('Array')返回一个函数接收下一个参数([])

    这就引出一个问题,上述函数只有两个形参,我们return一个函数,如果有多个形参呢,那就要多层嵌套了;我们希望每个参数都可以单独传入,这样多次使用的可以相对固话的参数,就可以实现再次封装了。是否可以实现一个通用的柯里化函数,实现柯里化方法?

    通用柯里化函数封装

    这里用一个参数比较多的示例讲解

    function sum(a,b,c,d,e) {
      return a + b + c + d + e
    }
    sum(1,2,3,4,5)
    

    这里的sum有5个参数,我们仿照上面的isType('Array')([])希望调用时是这样酱紫的

    function sum(a,b,c,d,e) {
      return a + b + c + d + e
    }
    // 通用柯里化
    function currying(fn) {
      //
      return function () {
        // 
      }
    }
    const getSum = currying(sum)
    console.log(getSum(1)(2,3)(4)(5))
    // 或者
    currying(sum)(1)(2,3)(4)(5)
    

    每次传入都不一定,保证直到5个都传齐全时才最终返回需要的结果

    开干

    function currying(fn, arr = []) {
      // (function(a,b,c,d,e) {}).length 获取传入函数的形参的个数
      let fnParamsLen = fn.length 
       // 高阶函数
      return function (...args) {
        let concatArr = [...arr, ...args]
        if (concatArr.length < fnParamsLen) {
          // 递归 不停的产生函数
          return currying(fn, concatArr) 
        } else {
          return fn(...concatArr)
        }
      }
    }
    // 测试
    // 1. 5参数求和
    const getSum = currying(function (a,b,c,d,e) {
      return a + b + c + d + e
    })
    console.log(getSum(1)(2,3)(4)(5)) // 15
    // 2. 类型判断
    const getType = currying(function (type,value) {
      return Object.prototype.toString.call(value) === `[object ${type}]`
    })
    console.log(getType('Array')('')) // false
    console.log(getType('Array')([])) // true
    

    扩展·封装ajax的get和post请求

    封装ajax的get和post请求,每次请求都需要传入请求类型type,因此可以考虑柯里化固定这个参数

    function Ajax() {
      this.xhr = new XMLHttpRequest()
    }
    
    // 核心处理器
    Ajax.prototype.open = function(type,url,data,callback) {
      this.onload = function() {
        callback(this.xhr.responseText, this.xhr.status, this.xhr)
      }
      this.xhr.open(type, url, data.async);
      this.xhr.send(data.paras);
    }
    
    // 在内存中推入一个get post方法,传入固定的第一个参数type,分别为get和post
    'get post'.split(' ').forEach(function(type) {
      Ajax.prototype[type] = currying(Ajax.prototype.open,type)
    })
    
    var xhr1 = new Ajax()
    // 调用柯里化后的函数,传入余下的参数url,data,callback
    xhr1.get(url,data,callback)
    
    var xhr2 = new Ajax();
    xhr2.post(url,data,callback);
    

    总结,柯里化是函数链式编程的一种运用,里面涉及到闭包,因此其缺点也是非常明显的,总结如下

    优点:1. 入口单一,易于测试和复用; 2. 易于定位bug,可以确定是哪个参数在哪个环节出了问题。

    缺点:函数嵌套,占用内存,效率低,毕竟每个function都会生成一个单独的作用域,都会在调用栈中占据一块内存空间。


    起源地下载网 » 柯里化理解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元