JavaScript之偏函数

原文出自冴羽的博客

定义

计算机科学中,局部应用是指固定一个函数的一些参数,然后产生另一个更小元的函数,什么是元?元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数

下面是个简单的例子

1
2
3
4
5
6
7
8
9
10
function add(a, b) {
return a + b;
}
// 执行add函数,一次传递两个参数
add(1, 2);

// 假设有一个partial函数可以做到局部应用
var addOne = partial(add, 1);

addOne(2);

柯里化和局部应用(偏函数)
这个片函数和柯里化如此的相似,两者之间的区别在哪呢?

柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个n元函数转换成n个一元函数。
偏函数则是固定一个函数的一个或者多个参数,也就是将一个n元函数转换成一个 n - x 元函数。

两者之间的的关系:柯里化是自动的偏函数的应用

partial

今天的目的就是模仿underscore写一个partial函数,比起curry函数,这个会显得简单些

1
2
3
4
5
6
function add(a, b) {
return a + b;
}
var addOne = add.bind(null, 1)

addOne(2) // 3

但是使用了bind之后,this的指向发生了改变,我们需要写一个不改变this指向的方法

第一版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   function partial(fn) {
var args = [].slice.call(arguments, 1);
return function() {
var newArgs = args.concat([].slice.call(arguments));
// 这里this指向obj
return fn.apply(this, newArgs);
}
}

function add(a, b) {
return a + b + this.value;
}

// var addOne = add.bind(null, 1); //传递null时在非严格模式下指向window
var addOne = partial(add, 1);

var value = 1;
var obj = {
value: 2,
addOne: addOne
}
obj.addOne(2); // ???
// 使用 bind 时,结果为 4
// 使用 partial 时,结果为 5

第二版

像curry函数可以使用占位符一样,我们希望partial函数也可以实现这个功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var _ = {};

function partial(fn){
var args = [].slice.call(arguments, 1);

return function(){
var position = 0, len = args.length;
for(var i = 0; i < len; i ++) {
args[i] = args[i] === _ ? arguments[position++] : args[i]
}
}
}
// 验证
var subtract = function(a, b) { return b - a; };
subFrom20 = partial(subtract, _, 20);
subFrom20(5);

underscore 和 lodash 都提供了 partial函数, 但是只有lodash提供了curry函数。