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.
To call a function inside another function, define the inner function inside the outer function and invoke it. When using the function keyword, the function gets hoisted to the top of the scope and can be called from anywhere inside of the outer function.
A scope is a region of the program and broadly speaking there are three places, where variables can be declared: Inside a function or a block which is called local variables, In the definition of function parameters which is called formal parameters. Outside of all functions which is called global variables.
Invoking a JavaScript Function The code inside a function is not executed when the function is defined. The code inside a function is executed when the function is invoked. It is common to use the term "call a function" instead of "invoke a function".
In Python, any written function can be called by another function. Note that this could be the most elegant way of breaking a problem into chunks of small problems.
The function sum
returns a function, which we refer to as f
.
The function f
also returns a function: in fact, the function f
returns itself.
When the function f
is defined inside of sum
, it gets permanent access to all variables currently visible in the scope chain. Here, it includes the locally-defined variable sum
(the local running sum tally) and f
(the function itself). (A "closure" is what we call the functional code of f
along with all its in-scope variables.)
Because f
returns itself, you can chain f
with repeated calls:
var this_is_f = sum(1); var same_f_again = this_is_f(2); var f_a_third_time = same_f_again(3);
Or simply:
sum(1)(2)(3);
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-new f
with a new local sum
in its scope (here, I mean the local sum
defined on the first line of the function named sum
). However, calling the function sum
does not clobber any old f
, because each call to sum
instantiates a new f
(and knows nothing about any other f
s that have been created on prior calls to sum
). That way, you can have multiple tallies running:
var first_tally = sum(1)(2); // first: 3 var second tally = sum(4)(5); // second: 9 first_tally(3); // first: 6 second_tally(6); // second: 15
The reason you're able to see a meaningful result at any time is that f
stingifies to the value of sum
, 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:
function add(x,y) { return x + y; }
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:
function add2(x) { return add(2, x); }
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:
function add(x) { return function(y) { return x + y; } }
Now you can create a function add2
by currying the function add
:
var add2 = add(2); add2(1); //=> 3
The normal function and the curried one have equivalent computations where:
add(1, 2) === add(1)(2)
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.
function sum(a) { // begin closure var sum = a; // kept in memory... function f(b) { sum += b; //...because you use it here return f; } f.toString = function() { return sum }; return f; } // end closure
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:
function add(a) { var sum = a; return { plus: function(b) { sum += b; return this; }, sum: function() { return sum; } } } add(1).plus(2).plus(3).sum(); //=> 6
In your code the returned function f
acts as plus
and toString
as sum
which retrieves the value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With