函数式编程--偏函数(Partial Application)

648 阅读3分钟

基本概念

偏函数(英文:Partial Application),也叫部分应用。是指在计算机科学中,固定多元函数的部分参数,并返回一个可以接受剩余部分参数的函数的转换过程。

举个简单的例子

function multi(a,b){
    return a * b
}

multi(2,5) // 10

使用偏函数,固定第一个参数

import _ from 'loadash'

var double = _.partial(multi,2)

double(5) // 10

目标功能

减少重复传参,提高函数的适用性

偏函数是指固定部分已知参数,同时返回一个接受剩余参数的函数。目的一方面是为了减少重复传参数;另一方面是为了降低函数的通用性、提高函数的适用性。

看下面例子

function square(i) {
    return i * i;
}

function double(i) {
    return i *= 2;
}

function map(handeler, list) {
    return list.map(handeler);
}

// 数组的每一项平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]);
// ......

// 数组的每一项加倍
map(double, [1, 2, 3, 4, 5]);
map(double, [6, 7, 8, 9, 10]);
map(double, [10, 20, 30, 40, 50]);

map函数需要两个参数,如果应用比较频繁,就会频繁的重复传参,比如上面例子中的square和double。其实,square和double是相对固定的,可以利用偏函数创建两个适用于上述场景的函数mapSquare和mapDouble,减少重复传参的问题。

import _ from 'loadash'

var mapSquare = _.partial(map, square)
var mapDouble = _.partial(map, dobule)

// 数组的每一项平方
mapSquare([1, 2, 3, 4, 5])
mapSquare([6, 7, 8, 9, 10])
mapSquare([10, 20, 30, 40, 50])
// ......

// 数组的每一项加倍
mapDouble([1, 2, 3, 4, 5])
mapDouble([6, 7, 8, 9, 10])
mapDouble([10, 20, 30, 40, 50])

固定执行环境上下文

Function.prototype.bind方法是一个典型的偏函数应用。一起回顾一下bind方法的语法和定义。

bind()方法的语法:


function.bind(thisArg[, arg1[, arg2[, ...]]])

bind方法的定义:

bind() 函数会创建一个新的绑定函数(bound function,BF)。这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

完全符合偏函数的特征: 1、固定部分参数 2、返回一个接受剩余参数的新函数

应用实例

在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。

function LateBloomer() {
    this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 在 1 秒钟后声明 bloom
LateBloomer.prototype.bloom = function() {
    window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
    console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用 'declare' 方法

### 实现原理 一起分析一下偏函数(Partial Function)的实现过程。

首先,偏函数也是一个高阶函数,其实现符合高阶函数的模型:

  • 接受一个或多个函数作为输入
  • 输出一个函数

它需要一个函数作为第一个参数,并且返回值也是一个函数


function partial(fn){
    return function restFn(){
        // ...
    }
}

其次,偏函数创建时可以固定部分参数,也即除第一个参数fn以外的参数,都是预置参数。

function partial(fn){
    var args = [].slice.call(arguments,1)
    return function restFn(){
        // ...
    }
}

最后,接受剩余参数,并和预置参数整合,作为fn的参数执行应用。

function partial(fn){
    var args = [].slice.call(arguments,1)
    return function(){
        var rest = [].slice.call(arguments)
        return fn.apply(null, args.concat(rest))
    }
}

对应的es6实现代码

const partial = (fn,...args)=>(...rest)=>fn.apply(null, args.concat(rest))

偏函数(Partial Application) 与 柯理化(Currying)的异同

1、它们都是高阶函数 2、它们都是把多元函数转换成更低元的函数 3、偏函数只返回一次接受剩余参数的函数,柯理化会追述到所有参数补齐才会真正执行 4、柯理化就是自动化的偏函数应用

参考资料

dev.to/ycmjason/ho…

developer.mozilla.org/zh-CN/docs/…

baike.baidu.com/item/%E9%AB…

www.html.cn/archives/77…

blog.csdn.net/weixin_3412…

juejin.cn/post/684490…