偏函数、高阶函数、柯里化、匿名函数

好久没写博客,从一道题目开始吧

实现一个sum函数,支持sum(1,2)和sum(1)(2)两种调用方式

说实在的,没啥难的,很简单写出来

function sum(a, b) {
  if (b) {
      return a + b;
  }   
  return (b) => a+b; 
}

或者用ES6的展开操作符,或者是arguments

function sum(...args) {
  if (args.length === 2) {
      return args[0] + args[1];
  } 
  if (args.length === 1) {
      return (b) => b + args[0]
  }   
}

考察的内容其实就是函数作为返回值,简单涉及到了闭包,还有arguments的判断,ES6展开操作符的剩余语法。然后可以继续发散,ES6熟不熟啊,闭包理解到不到位啊,arguments是不是数组,怎么转成数组……

不写了,不写了,展开能问的太多了。今天主要的目的是写一写函数相关,解释一下那些常见的概念:偏函数、高阶函数、柯里化、匿名函数。

偏函数

偏函数(Partial application),wiki上解释

In computer science,partial application(orpartial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

大概意思,偏函数会固定一些参数,产生更少参数的函数。

改一下上面的题目:

实现一个sum函数,可以支持固定一个参数的的调用方式;如

var sum1 = sum(1);

sum1(2) // 3

function sum(base) {
    return (num) => base + num;
}
var sum1 = sum(1);

或者使用bind

function sum(a, b) {
    return a + b;
}
var sum1 = sum.bind(null, 1);

这里可以引申出bind的用法,bind和call的区别,call和apply的区别。就不多赘述了。之前写过一篇博客bind、apply与call

高阶函数

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: 接受一个或多个函数作为输入 输出一个函数

wiki链接

可以这么理解,如果可以接受函数作为入参和出参的,可以认为支持高阶函数。JS中,函数是一等公民,各种高阶函数随处可见,很容易写一个例子:

const print = console.log;
const foo = (a , b, printFn) => a + b;

foo(1, 2, print);

说个题外话,有这样一道题目:

[1,2,3].map(parseInt)

我感觉这个题目有点考api记忆了,不过如果熟练的话也就还好。map是一个高阶函数,接受一个函数作为参数,他会给入参函数传入两个值,item和index。parseInt接受两个参数,数值和进制。也就是执行

parseInt(1, 0);
parseInt(2, 1);
parseInt(3, 2);

答案[1, NaN, NaN]我就不多说了。

柯里化

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

wiki链接

大概定义一下,可以认为curry之后的函数,可以接受一个参数,之后返回一个接受剩余参数的函数。

期望可以这么执行:

var add = curry((a, b) => a + b);

add(1, 2); // 可以正常运行
var add1 = add(1);
add1(2); // 也可以这么使用

可以发现,这不就是上面的题目吗?当然,稍有不同的地方在于,这里是通过curry把一个正常函数curry化。我们也来实现一个。

function curry (fn) {
    return (...args) => {
       if (args.length === fn.length) {
          return fn.apply(null, args); 
       }
       return fn.bind(null, args[0])
    }
}

很明显,上面用到了偏函数的实现去做了curry,也可以不使用bind,在使用别的方法之前,加一些限定条件。

上面的curry简单处理了一个参数的情况,我们希望curry更智能一些,假设函数有10个参数,如果传了5个,又传了3个,又传了2个,只有最后一次才会返回结果。

// 为了使用递归,写了一个helper
function helper (fn, ...args) {
    if (args.length === fn.length) {
        return fn.apply(null, args);
    }
    return (...argsMore) => createFn(fn, ...args, ...argsMore);
}

function curry (fn) {
    return (...args) => createFn(fn, ...args);
}

多说一个概念,thunk,阮一峰老师在他的博客里介绍过Thunk 函数的含义和用法,大家可以看一下,其实也就是curry的一处用法。

匿名函数

在计算机编程中,匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数子程序,普遍存在于多种编程语言中。

wiki

匿名函数相对命名函数而言,就是一些一次性使用的场景下,比如一些事件处理函数,可能只执行一次,可以使用匿名函数。

这个概念经常用,举个例子。

[1,2,3].map((item, index) => `item ${index}: ${item}`);

匿名函数的概念很简单,上面写的箭头函数容易考察一些this指向的问题。

比如为什么箭头函数不能使用new,怎么做到绑定this的。

这个问题,我打算留到下一篇写。嘿嘿嘿😝

完。感谢阅读。

posted @ 2019-03-27 22:51  liuyongjia  阅读(1260)  评论(0编辑  收藏  举报