serendipityApe / javascriptPromotion

资深前端必备的编码问题
3 stars 0 forks source link

实现柯里化 #2

Open serendipityApe opened 2 years ago

serendipityApe commented 2 years ago

题目

implement curry() 柯里化是js中一个常用的概念,接下来我们来尝试写出一个 curry 函数,接收一个函数然后返回该函数的柯里化

例子


const join = (a, b, c) => {
return `${a}_${b}_${c}`
}

const curriedJoin = curry(join)

curriedJoin(1, 2, 3) // '1_2_3'

curriedJoin(1)(2, 3) // '1_2_3'

curriedJoin(1, 2)(3) // '1_2_3'

> 答题

//答案1 function curry(func) { return function innerFunc(...args) { //func为原函数,innerFunc为新函数 //如果新函数的参数比原函数多或相等则执行原函数 if(args.length >= func.length){ return func(...args) }else{ //否则一直累加新函数的参数 return function(...next){ return innerFunc(...args,...next) } } } }

或者

//答案2 function curry(func) { return function innerFunc(...args) { if(args.length >= func.length){ return func(...args) //return func.apply(this,args) }else{ //这里的bind作用是参数继承而不是改变this(我们应该清楚function内this都为window) return innerFunc.bind(this,...args) } } }

> 补充

我觉得执行curry函数后,我们理应可以通过.length来判断剩余的参数数量,比如下面这样

const join = (a, b, c) => { return ${a}_${b}_${c} }

const curriedJoin = curry(join)

curriedJoin(1, 2, 3).length // 0

curriedJoin(1).length // 2

curriedJoin(1, 2).length // 1

但是事实却是无论是答案1还是答案2都恒定返回0
至于原因是什么,让我们先来了解一下Function.length

//length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。 console.log(Function.length); //1

console.log((function() {}).length); //0 console.log((function(a) {}).length); //1 console.log((function(a, b) {}).length); //2

//形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。 console.log((function(...args) {}).length); // 0, //剩余参数(...args)为0

console.log((function(a, b = 1, c) {}).length); // 1 //b=1声明了默认值,第一个具有默认值之前的参数个数为1

看到这里,我想大家都明白了:
在答案1的 `return function(...next){
        return innerFunc(...args,...next)
      }`我们使用了剩余参数(...next),因此恒定为0。
而在答案2中原因却是第二行的  `return function innerFunc(...args) {`

//看下面例子

const join = (a, b, c) => { return ${a}_${b}_${c} } console.log(join.bind(this,1).length) //2

function join2(...args){ return ${args[0]}_${args[1]}_${args[2]} } console.log(join2.bind(this,1).length) //0 //上面的两个函数区别就在与bind的原函数,如果原函数使用了剩余参数(...args),那么使用bind后length也为 //而在答案2里,我们的原函数innerFunc.length显然为0

最后,怎么让curry能够通过.length查看剩余参数呢

function curry(func) { return function innerFunc(...args) { if(args.length >= func.length){ return func(...args) }else{ var boundLength = func.length-args.length var boundArgs = []; for (var i = 0; i < boundLength; i++) { boundArgs.push('$' + i); } var binder= function(...next){ return innerFunc(...args,...next) } var bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); return bound
} } }


> 参考链接:
 [https://github.com/Raynos/function-bind/blob/master/implementation.js](https://github.com/Raynos/function-bind/blob/master/implementation.js)
[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/length](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/length)