前端面试题——函数柯里化

目录

1.问题解决

2.什么是柯里化?

3.柯里化有什么用

4.柯里化函数


准备前端面试的朋友们一定不会对这道题目陌生:

  • 写一个sum函数,让sum(2)(3)返回5

相信很多人都可以很轻松地写出代码。但是背后的知识你真的了解么?

1.问题解决

首先,我们还是先解决上面的问题吧。

相信很多人都听说过,“在JavaScript中,函数一等公民(functions as first-class citizens)”这个说法。这就意味着,函数可以作为别的函数的参数、函数的返回值、传给另一个变量等等。

在sum(2)(3)中,想要xxx(3)能执行,相比sum(2)一定是一个函数。函数执行之后还是一个函数,顺着这个思路,很容易想到解法:

function sum(a) {
  return function (b) {
    return a + b;
  }
}

这就是柯里化!

2.什么是柯里化?

柯里化是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下参数的新函数的技术。

比如说,sum(2, 3)可以转换成sum(2)(3)。

3.柯里化有什么用

有的人会问,我直接用sum(2, 3)不行么,为什么要“费力不讨好”地转换成sum(2)(3)?

柯里化的好处在于,调用函数的时候,如果某一个参数在每次调用中都相同,可以避免重复传入这个参数。

举个简单的例子吧:有一个函数叫做isSameUser(user1, user2),这是用来检测user1和user2是不是相同的user。

但是在某个文件之中,我们只想检测user2是否是loggedInUser,那么我们就可以固定一个参数:

var isLoggedInUser = curry(isSameUser);

然后只需要调用isLoggedInUser(user2)就可以了。

例子比较简单,实际的工作中遇到更加复杂的情形,使用柯里化可以避免不停地传入相同的参数。

4.柯里化函数

红宝书上给了我们一个柯里化函数实现的方法:


function curry(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var innerArgs = Array.prototype.slice.call(arguments);
    var finalArgs = args.concat(innerArgs);
    return fn.apply(null, finalArgs);
  }
}

这个实现方法中,调用curry的时候,传入的第一个参数是你想柯里化的函数,比如sum,之后的参数是sum这个函数的参数,记为args。在返回函数中,再传入剩下的参数,记为innerArgs,两者拼接到一起成为finalArgs,最后使用fn调用。

使用方法可以是这样:

function sum(a, b) {
  return a + b;
}
// 刚开始不传入参数,之后一次传入
var currySum = curry(sum);
console.log(currySum(2, 3));
// 刚开始传入一个参数,之后传入剩下参数
var currySum = curry(sum, 2);
console.log(currySum(3));
// 刚开始传入所有参数,之后不传参数
var currySum = curry(sum, 2, 3);
console.log(currySum());

不过这个方法只可以把参数列表分成两次传递进去。如果遇到sum(2, 3, 4)转为sum(2)(3)(4),或者更复杂的情形就无能为力。

那么有没有一种实现方法可以自动检测参数是否传递完毕,然后再执行呢?看下面这段代码:

function sum(a, b, c) {
  return a + b + c;
}

function curry(fn) {
  return function currify() {
    const args = Array.prototype.slice.call(arguments);
    return args.length >= fn.length ?
      fn.apply(null, args) :
    currify.bind(null, ...args);
  }
}

var currySum = curry(sum);

 在curry这个函数当中,返回一个叫做currify的函数,之所以不能直接使用匿名函数,是因为我们还需要在内部递归调用currify。之后获取参数存在args中。关键的步骤来了:如果args的长度大于fn.length(函数的length就是函数期望传入参数的长度),这说明已经传入足够的参数,就可以使用fn.apply执行fn;但是,如果传入的参数个数不足,那么就会返回currify.bind(null, ...args);把当前的参数列表传给currify这个函数。(如果对bind不理解可以阅读我的《前端面试题——自己实现bind》)。

大功告成,我们来测试一下把:

console.log(currySum(1)(2)(3));
console.log(currySum(1, 2)(3));
console.log(currySum(1)(2, 3));

以上三个输出都应该是6。

猜你喜欢

转载自blog.csdn.net/m0_68997646/article/details/128772667