Could any one explain how this function alert, when more no of brackets of parameters are passed. I could not able to understand it clearly.
function sum(a) {
var sum = a
function f(b) {
sum += b
return f
}
f.toString = function() { return sum }
return f
}
alert( sum(1)(2) ) // 3
alert( sum(5)(-1)(2) ) // 6
alert( sum(6)(-1)(-2)(-3) ) // 0
alert( sum(0)(1)(2)(3)(4)(5) ) // 15
The first time your function is called, the first value is stored in sum
. After that function f(b)
will be returned, maintaining the provisional result in sum
. With each consecutive call you execute function f
- you perform sum += b
and return f again. If a string context is required (such as in the alert
, or a console.log
) f.toString
is called instead, returning the result (sum
).
function sum(a) {
var sum = a
function f(b) {
sum += b
return f //<- from second call, f is returned each time
// so you can chain those calls indefinitely
// function sum basically got "overridden" by f
}
f.toString = function() { return sum }
return f //<- after first call, f is returned
}
Explanation:
alert( sum(6)(-1)(-2)(-3) ) // 0
/\ function sum called, f returned
/\ the returned function f is called, f returns itself
/\ again
/\ and again
/\ at last, alert() requires string context,
so f.toString is getting invoked now instead of f
The thing to look at is this piece of code
function f(b) {
sum += b
return f
}
This function returns reference to itself so it can be called as many times as possible. An important thing about it is that it has a tostring function that gets called and since tostring is defined inside function sum()
it has access to the variable sum
and its value current value (that is changed by f()
)
alert
expects a string. If it doesn't get a string, it will attempt to convert whatever object it receives (and a function is a type of object) into one. If the object has a toString
method, then that will be called to perform said conversion.
The sum
and f
functions always return the f
function, you can invoke it infinite times without getting a result other than the function.
The value is only returned by the overwritten toString
method of f
(which actually returns a number):
console.log( sum(1)(2) ) // Function(){}
console.log( sum(1)(2).toString() ) // 3
The alert
function implicitly invokes the toString
method when it casts its arguments to strings.
It doesn't work as intended in all of the cases... The problem is that .toString
is expected to return a string, so string methods in provided implementation, would not work, e. g. sum(2)(3).split()
will cause an error.
Although we might assume sum()
result will always be expected to be a number, it might not be true in some cases and might be hard to debug, e. g. I noticed the issue when I was testing code initially written with .toString
only on jsbin.com (it does split
on console.log argument internally, overriding it).
Instead, .toString
should look like return String(result);
. Good thing that .toString
(when there's no .valueOf
or modern Symbol.toPrimitive
) will handle primitives conversion, so code expecting a Number will work as well. The possible issue here might be "double" conversion caused by this.
Better solution might be to use either pair of .toString
and .valueOf
or just a single Symbol.toPrimitive
if you're only targeting modern browsers.
Example using Symbol.toPrimitive
:
function sum(a) {
let result = a;
function f(b) {
result += b;
return f;
}
f[Symbol.toPrimitive] = hint => hint === 'string' ? String(result) : result;
return f;
}
Example using .toString
and .valueOf
pair.
function sum(a) {
var result = a;
function f(b) {
result += b;
return f;
}
// avoiding double conversion which will happen in case of .toString
f.valueOf = function() { return result; };
f.toString = function() { return String(result); };
return f;
}