基本概念
偏函数(英文: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、柯理化就是自动化的偏函数应用