Hope somebody finds the time to explain little about functions in functions and scoping. I am trying to understand little more on functions and scope of variables and found a quite good tutorial, but this part I just do not get.
The task:
Create a function sum that will work like that: sum(a)(b) = a+b
and accepts any number of brackets. Examples:
sum(1)(2) == 3
sum(5)(-1)(2) == 6
The solution:
function sum(a) {
var sum = a;
function f(b){
sum += b;
return f;
}
f.toString = function() { return sum };
return f; //line 12
}
alert( sum(1)(2) ); // 3e
The Explanation:
To make sum(1)
callable as sum(1)(2)
, it must return a function.
The function can be either called or converted to a number with valueOf
.
The solution is really self-explanatory:
My interpretation:
This f
in function f(b)
returned to the scope, which is from line 02 - 12.
The f
in f.toString
, is the currently returned f
from function(b)
The next return f
returns to the scope which is outside the function sum(a)
.
Problem:
I cannot figure out, where I need to think differently, because like I described above, the function would not be called again, so where is the part of the code, that make the 'several parentheses' possible?
Moreover, did I correctly assume where the f
s are returned? Would be great if somebody would give some explanations.
Let's step through the function line by line:
This part is pretty self-explanatory; we have a function named
sum
which accepts an argumenta
.Here we have a local variable called
sum
that is set to the value of the argument that is passed in.This is an inner function called
f
that accepts an argument calledb
. Thisb
is then added to the value ofsum
from the outer scope (i.e., inside the scope of the function that is also calledsum
). After that, the function returns itself.This is the fun part! When you normally
console.log
a function, it will just spit out the function's source. Here we are redefining thetoString
method to instead be a function that spits out the value ofsum
. This is why you end up seeing the running total that is displayed instead of the function's source even though what you are returning is still a function.Finally we have:
So you basically have a function
sum
, returning a function that returns itself. The functionsum
basically sets everything up, but after callingsum(3)
, you then work withf
, which you repeatedly call.So the first time you call
sum
, you essentially get back this function:In this context, the value of
sum
will be the value ofa
that you passed into the initial call of the functionsum
. However, since thetoString
off
has been redefined, you only see the value3
. Then let's say you dosum(3)(4)
.You get back, as before:
But then you are actually calling
f
with an argument of4
(basicallyf(4)
). Sincef
is an inner function, it has full access to the scope of its parent function. This parent function (sum
) is maintaining the the running total in a variable calledsum
, which is accessible tof
. So when you now callf(4)
, you haveb
set to4
andsum
having a value of3
:So with each subsequent pair parentheses you are making repeated calls to the same
f
which is maintaining a running tally.Another way is to think of
sum
as a kind of factory that can give you differentf
's, all of which keep their own running tallies (basically behaving like accumulators):The function
sum
returns a function, which we refer to asf
.The function
f
also returns a function: in fact, the functionf
returns itself.When the function
f
is defined inside ofsum
, it gets permanent access to all variables currently visible in the scope chain. Here, it includes the locally-defined variablesum
(the local running sum tally) andf
(the function itself). (A "closure" is what we call the functional code off
along with all its in-scope variables.)Because
f
returns itself, you can chainf
with repeated calls:Or simply:
It is important to note that in my first example, I don't create new functions; instead, I just refer to the exact same function object with three different identifiers.
Each call to
sum
creates a brand-newf
with a new localsum
in its scope (here, I mean the localsum
defined on the first line of the function namedsum
). However, calling the functionsum
does not clobber any oldf
, because each call tosum
instantiates a newf
(and knows nothing about any otherf
s that have been created on prior calls tosum
). That way, you can have multiple tallies running:The reason you're able to see a meaningful result at any time is that
f
stingifies to the value ofsum
, instead of showing you its source code.If you take this code and simplify it to the bare minimum I would be easier to understand. Take a function
add
that sums only 2 numbers:The above is a "normal" function. If you don't pass all the arguments you'll get unexpected results.
Now, if you want a function that adds 2 to any number, you could partially apply an argument to
add
, for example:But in JavaScript we have first class functions (objects that can be passed around), so a function can take functions as inputs and return other functions. This is where "currying" comes in handy. While "partial application" lets you fix function arguments, "currying" takes a function of many arguments and breaks it down into a function of a single argument that returns another function of one single argument until all the arguments have been evaluated, in order, and then returns the result. For example:
Now you can create a function
add2
by currying the functionadd
:The normal function and the curried one have equivalent computations where:
This is what makes "several parentheses" possible.
In JavaScript "scope" and "closure" refer to functions but they're different concepts. A "scope" determines the reach/privacy of your variables while a "closure" lets you encapsulate code and carry it around. In the curried function above the variable
x
is kept in memory through closure because it's referenced inside the returned function object.A particular limitation of "currying" is that you can't have functions of dynamic arity; the number of arguments must be fixed. This is not the case in the code you post where your
sum
function must be able to add a new number to the previous sum indefinitely.Similarly to "currying" there's the idea of "generators"; a pattern to model sequences of lazy computation, in this case just adding a number to the previous sum on demand.
A different (maybe more clear) way to express your code would be to return an object to chain the computations, where you can either add a new number to the previous sum, or get the total sum so far:
In your code the returned function
f
acts asplus
andtoString
assum
which retrieves the value.There are 3 concepts being played here
f(a)(b)(c)
). There's no need to save a handle to the function to call it later.I'll give you a run-down and then add some extra explanations afterwards
The concept behind closures is quite easy to understand but it's application make's your head spin until you get used to the idea that there is no function scope in javascript, there is closures and these are powerfull critters indeed
Functions are first class objects. What does this mean? I'm not sure myself but let me explain with some further examples:
Don't take this word for word. Go look for other people's code, check the Crockford and ask any and all questions that come up
Chaining
Notice that:
f
refer to the same function.sum
while the others callf
.sum
returnsf
andf
returns itself, in other words, whenever you write()
, you're returning the samef
function.In summary:
Persistence
That being said, the main question is: how the state of the sum is able to persist along those function calls? This is where this famous "closure" thing comes in. I'm not going to talk about this concept in details, Google can help far better. In a few words, it's about the relationship between
f
and thesum
variable. Indeed, from the moment they come to life, thanks to the first function call, they'll share the same private context for the rest of their existence. Consequently, every subsequent call consists in modifying the existingsum
variable throughf
:Result
Finally,
toString
is called automatically when a string is expected in place off
:Note that you could also use
valueOf
. It might be a bit more consistent since Function already owns a version of thetoString
method in order to get the function declaration itself.Bonus
That's it, hope this explanation was clear enough. As a conclusion, here is a similar approach (to get the final result, just put an empty pair of brackets at the end of the queue):
sum
within the global scope.sum
and changes scope to inside the function.a
andsum
and the closure functionf
are all defined in the function's scope - with thesum
variable hiding the function definition from the global scope.toString
bit here as its not important till later (when it gets very important).sum
finally returns a reference to the closure functionf
to the global scope as an anonymous function (sincef
can only be referenced by name from within its containing function's scope).sum(1)(2)
is the same as(sum(1))(2)
and sincesum(1)
returnsf
then this can be reduced to(f)(2)
or, more simply,f(2)
.f(2)
adds2
tosum
-sum
is defined in two scopes: as a function in the global scope; and as a variable (currently assigned the value of1
) within that function which hides the definition from the global scope - so, inf
, thesum
variable is set to1+2=3
.f
, the function returns itself.f(2)
returnsf
(although the named functionf
cannot be referenced in the global scope and, again , is treated as an anonymous function)alert()
is processed and the anonymous function (referred to asf
) is converted to a string to be alerted; normally whenalert()
is called on a function it will display the source for the function (try commenting out Line 10 to see this). However, sincef
has atoString
method (Line 10) then this is invoked and the value ofsum
(as defined in the function containingf
) is returned and3
is alerted.