JavaScript 专题系列第十四篇,讲解偏函数以及如何实现一个 partial 函数git
维基百科中对偏函数 (Partial application) 的定义为:github
In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.app
翻译成中文:函数
在计算机科学中,局部应用是指固定一个函数的一些参数,而后产生另外一个更小元的函数。this
什么是元?元是指函数参数的个数,好比一个带有两个参数的函数被称为二元函数。翻译
举个简单的例子:code
function add(a, b) { return a + b; } // 执行 add 函数,一次传入两个参数便可 add(1, 2) // 3 // 假设有一个 partial 函数能够作到局部应用 var addOne = partial(add, 1); addOne(2) // 3
我的以为翻译成“局部应用”或许更贴切些,如下所有使用“局部应用”。排序
若是看过上一篇文章《JavaScript专题之柯里化》,实际上你会发现这个例子和柯里化太像了,因此二者究竟是有什么区别呢?递归
其实也很明显:ip
柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。
局部应用则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。
若是说二者有什么关系的话,引用 functional-programming-jargon 中的描述就是:
Curried functions are automatically partially applied.
咱们今天的目的是模仿 underscore 写一个 partial 函数,比起 curry 函数,这个显然简单了不少。
也许你在想咱们能够直接使用 bind 呐,举个例子:
function add(a, b) { return a + b; } var addOne = add.bind(null, 1); addOne(2) // 3
然而使用 bind 咱们仍是改变了 this 指向,咱们要写一个不改变 this 指向的方法。
根据以前的表述,咱们能够尝试着写出初版:
// 初版 // 似曾相识的代码 function partial(fn) { var args = [].slice.call(arguments, 1); return function() { var newArgs = args.concat([].slice.call(arguments)); return fn.apply(this, newArgs); }; };
咱们来写个 demo 验证下 this 的指向:
function add(a, b) { return a + b + this.value; } // var addOne = add.bind(null, 1); var addOne = partial(add, 1); var value = 1; var obj = { value: 2, addOne: addOne } obj.addOne(2); // ??? // 使用 bind 时,结果为 4 // 使用 partial 时,结果为 5
然而正如 curry 函数可使用占位符同样,咱们但愿 partial 函数也能够实现这个功能,咱们再来写第二版:
// 第二版 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] } while(position < arguments.length) args.push(argumetns[position++]); return fn.apply(this, args); }; };
咱们验证一下:
var subtract = function(a, b) { return b - a; }; subFrom20 = partial(subtract, _, 20); subFrom20(5);
值得注意的是:underscore 和 lodash 都提供了 partial 函数,但只有 lodash 提供了 curry 函数。
JavaScript专题系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript专题系列预计写二十篇左右,主要研究平常开发中一些功能点的实现,好比防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特色是研(chao)究(xi) underscore 和 jQuery 的实现方式。
若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star,对做者也是一种鼓励。