Assume i have test.js file with codeI promised him an answer as a blog post, so here it is.
function createAdder(x) { return function(y) { return x + y; } } var add2 = createAdder(2); //<-------LINE 1 var add5 = createAdder(5); //<-------LINE 2 alert(add2(10)); //<------------LINE 3 alert(add5(10)); //<------------LINE 4... My doubt: what's actually happening in the LINE 1-4?
Currying
What we see here is something called function currying. It's common in mathematics and is named after one of its inventors — Haskell Curry. In short, currying converts a single function that takes in multiple arguments to multiple functions each of which takes in a single argument. This is particularly useful when caching the intermediate steps for later reuse with different parameters is of use. The classic example is that of operating on two numbers:function divide(a) { return function(b) { return b/a; } } var divide_2 = divide(2); var divide_3 = divide(3); alert(divide_2(6)); // alerts 3 alert(divide_3(6)); // alerts 2In the above code,
divide_2
is a function that takes in one argument and divides it by 2, returning the result. This is a fairly trivial and not very useful example because there are easier ways to divide a number by two. It becomes more useful though, when we need to do a bunch of expensive processing to get to each of the inner results. Consider this code instead:
function hash(salt) { // do some expensive processing on salt var hash1 = process(salt); return function(data) { // cheap processing of data with hash1 return data + hash1; }; } var sign1 = hash(salt1); // sign1 is a function that signs data with salt1 var signature = sign1(some_data);In the above code, the outer function does a bunch of expensive processing, and its result is stored in the
hash1
variable. This variable is available to the inner function whenever it is called because of the closure that's created. When the inner function is called, it simply uses the value of hash1
without having to redo the processing.
Now we could have called process()
externally and cached its result, but then the hash1
would be exposed. This may not be something we want to do either because it needs to be abstracted out, or because its value is sensitive.
Closures
This all works because of closures. In short, a variable will continue to exist as long as code that can see it can be run. In the above cases, the inner functions are returned and their references stored in global variables. This makes the lifetime of these inner functions global, ie, they will exist as long as their new containing scope exists. These functions do not, however, get the new scope, so the variables they can see are exactly what they could see when they were defined. In the divide example, the inner function sees the variablea
, therefore a
will exist for as long as the inner function exists.
When we create divide_2
, the value of a
is set to 2, and this is what the inner function (which is now stored in divide_2
) sees. When we create divide_3
, a new a
is created, this time with value 3, and a new inner function is created (which is now stored in divide_3
) and this function sees the new value of a
. This is a completely new execution scope than when divide(2)
was called.
So getting back to the example my friend asked about, this is what happens:createAdder(2)
: At this point, the argumentx
is set to the value 2, and the inner function is returned and stored inadd2
. This function remembers the value ofx
and uses it when it has to be called.createAdder(5)
: At this point, the argumentx
is set to the value 5. Note that this is a new invocation ofcreateAdder
and does not share the same memory as the first invocation, so these are two completely different variables namedx
, both living in different scopes.add2(10)
: At this point, the first inner function is called with the argument 10, which is stored iny
. This function remembers the value ofx
as 2 and computes2 + 10
and returns its valueadd5(10)
: The second instance of the inner function is called with the argument 10, which is stored iny
. This function remembers the value ofx
as 5 from when it was called, and computes5 + 10
and returns its value
function foo(x) { var g=function(y) { return x+y; }; x=x*x; return g; } var add_2 = foo(2); var add_3 = foo(3); alert(add_2(5)); // alerts 9 alert(add_3(5)); // alerts 14Notice that the value of
x
was changed after the inner function g
was defined, yet g
sees the new value of x
.
This is particularly important when you use a loop control variable inside a closure. The closure will see the last value of the loop control variable and not a different value on each iteration.
Update: 2010-03-04 t3rmin4t0r has a much more useful example of currying on his blog.
1 comments :
Thanks Philip. Now i got an idea about function variable scope and closure. Started learning front end technologies recently(Missed my opportunities to learn Web technologies at Yahoo!)
Post a Comment