一道add面试题的解法

函数柯里化

函数柯里化是指一个函数接收函数A作为参数,运行后能够返回新函数,这个新函数能够处理函数A的剩余参数。参照

// 简单实现,参数只能从右到左传递
function createCurry(func, args) {

    var arity = func.length;
    var args = args || [];

    return function() {
        var _args = [].slice.call(arguments);
        [].push.apply(_args, args);

        // 如果参数个数小于最初的func.length,则递归调用,继续收集参数
        if (_args.length < arity) {
            return createCurry.call(this, func, _args);
        }

        // 参数收集完毕,则执行func
        return func.apply(this, _args);
    }
}

柯里化思路解决最常见的一个面试题

add(1)(2)(3) == 6;
add(1, 2, 3)(4) == 10;
add(1)(2)(3)(4)(5) ==15;
add(1)(2)(3)(4)(5) + 1 == 16

这个是根据柯里化衍生出来的题目。add函数并不接收函数,它只是接收参数,它能够返回一个新函数,这个函数的功能和add一样,继续接收参数。所以我们只要定义一个函数,返回一个新函数,这个函数能够接收参数,并在toString时候转换为数字。

下面是网上给出的写法

function add () {

  let args = [].slice.call(arguments)
  
  function adder () {
    var _add = function () {
      args.push(...arguments)
      return _add
    }

    _add.toString = function () {
      return args.reduce((i, item) => i + item)
    }

    return _add
  }

  return adder(...args)
}

实际上,这个和柯里面化没有什么关系,应该说是链式调用的一种方式。由于我们要保存参数,第一次执行时返回的是内部的_add函数,_add因为使用了闭包,可以访问我们最开始生成的一个数组,每次把新的参数加入到数组中。使用toString保证在运算时会自动进行运算。

其他解法

bind方法也是返回一个函数,只是函数是隐式返回的。既然是返回函数形成链式调用,那么用bind也可以达到效果。下面是一种实现

function add () {
  var args = [].slice.call(arguments)

  add.toString = function () {
    return args.reduce((i, item) => i + item)
  }

  return add.bind(null,...args)
}

调用下看下

add(1)(2)(3)(4) // 10
add(1)(2)(3)(4) + 1 // function () { [native code] }1

结果只是部分达到了我们的预期。由于bind方法返回的是一个新方法,在console.log()时候可能针对bind进行了进一步执行,而在加法的隐式转换中则无法实现。因此改进下:

function add () {
  var args = [].slice.call(arguments)

  var func = add.bind(null, ...arguments)

  func.toString = function () {
    return args.reduce((i, item) => i + item)
  }

  return func
}


add(1)(2)(3)(4)
// ƒ add() {
//   var args = [].slice.call(arguments)

//   var func = add.bind(null, ...arguments)

//   func.toString = function () {
//     return args.reduce((i, item) => i + item)
//   }

//   return func
// }

add(1)(2)(3)(4) + 1 // 11

可以发现,隐式转换生效了,但我们log出来却是add函数本身。所以再给add加一层

function add () {
  var args = [].slice.call(arguments)

  var func = add.bind(null, ...arguments)

  add.toString = func.toString = function () {
    return args.reduce((i, item) => i + item)
  }

  return func
}
add(1)(2)(3)(4) + 1 // 11
add(1)(2)(3)(4) // 10

结果就可以实现了。这种bind方法实际上就是把原来的闭包存储的参数转换到从arguments中存储。由于bind要不停上创建函数,理论上性能会差一些,但代码更容易理解一些。

posted @ 2017-12-18 17:16  无梦灬  阅读(370)  评论(0)    收藏  举报