Identify how the function has been called in closure javascript

前提是你 提交于 2019-12-12 19:50:34

问题


Recently i faced one problem in hackerrank which has to calculate multiplication operation and has to return the answer. For example

function multiply(a,b) {
     return a*b;
}

Now here is the problem the function might call in different ways such as

multiply(4,5);
multiply(4)(5);
multiply(4)(5)(6);

I know we have to closure apporach for the second one which is multiply(4)(5). I had written code for that

function multiply(a,b) {
  return function(b) {
    return a*b;
  }
}

Now what if its been multiply function has been called with 3 arguments multiply(4)(5)(6). How can i identify how the function has been called and how can i write a common solution for all the inputs.

Any help is appreciated. Thanks


回答1:


To have an unlimited callchain you need some js trick:

function multiply(...args){
  //make the chain endless through using some recursive like stuff:
  const func = (...args2) => multiply(...args,...args2);
  //the chains endpoint:
  func.valueOf = function(){
     return args.reduce((a,b) => a*b);
  };
  return func;
}

The problem of such variable currying is that its infinite, so theres ( usually ) no way to end it.

multiply(1) // func :/

However in javascript its possible to assign methods to functions so we can easily somewhen call the method instead of the function to end the chain:

multiply(1)(2)(3).valueOf()

So you can simply do:

 console.log(
  +multiply(1,2,3)(4)(5),
  +multiply(1,2)
);

The + is equal to valueOf and is therefore neccessary to end the chain, however this call to valueOf is inferred by many operations in javascript ( all math operations like - * /).




回答2:


The Problem with this approach is the ambiguity of the returned value. It has to be a Number AND a function, both at the same time.

due to the valueOf() method you can simulate that behaviour, but I'd rarely reccomend to use that in production.

const wrap = (accumulator, value) => {
  let fn = (...args) => args.length? wrap(accumulator, args.reduce(accumulator, value)): fn;
  fn.valueOf = () => value;
  return fn;
}

let mul = wrap((a,b) => a*b, 1);

console.log(+mul(4,5));
console.log(+mul(4,5)(6));
console.log(mul(4,5) * 6);
console.log(+mul(4)(5,6)(7)(8,9)()(10));

//or maybe a different operation?
let add = wrap((a,b) => a+b, 0);

console.log(+add(1,2,3,4,5));
.as-console-wrapper{top:0;max-height:100%!important;}

Downside of this approach, it's made so flexible (and ambigious), it's pretty much useless since it's way too hard to grasp what result you'll get when using this somewhere inline. What if some functions you'll feed this into do a typecheck? Then you're in trouble.




回答3:


You're close, what you are trying to do is called currying, a technique that takes advantage of closures.

In this case, your function should only take one argument at a time:

function multiply(a) {
    return function(b) {
        return a * b;
    }
}

Now you could say

function multiplyBy5 = multiply(5);
console.log(multiplyBy5(4)); //would log 20

If you're only expecting at max 3 arguments you could do:

function multiply(a) {
    return function(b) {
        return function(c) {
            c = c || 1;
            return a * b * c;
        }
    }
}

Which makes c an optional argument. You can call that function by

multiply(4)(5)()

//or

multiply(4)(5)(6)


来源:https://stackoverflow.com/questions/47286900/identify-how-the-function-has-been-called-in-closure-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!