Reputation: 11120
Without a specific knowledge of closures, one can understand that the snippet:
function closure(){
var i = 1;
i++
var ofunc = function(){return i;};
return ofunc;
}
var x = closure()
x()
will return:
=> 2
The following snippet instead is very counter-intuitive:
function closure(){
var i=1;
i++
var ofunc = function(){return i;};
i++;
return ofunc;
}
var x = closure()
x()
as it will return:
=> 3
Despite the second increment operation (i++;
) follows ofunc
, when ofunc
is called via x
and by x()
, its scope is that of closure
. closure
being executed, i
evaluates to 3, hence the result.
An apparent solution is:
function closure(){
var i = 1;
i++
var ofunc = function(){return i;}(); //note the parenthesis
i++;
return ofunc;
}
var y = closure()
y //note the missing parenthesis
which returns again the more comforting:
=> 2
In this case, ofunc
was executed during its declaration, because of the final trailing ()
, using the current value of i
, 2. Therefore y
doesn't return a pointer to ofunc
to be executed with y()
, but the result of the execution of ofunc
.
Unfortunately this destroys all the power of closures, or at least so it seems to me. That is the possibilities of not executing the object function immediately and passing parameters (to its pointer) when executing it at a later time. In fact, if we add arguments to ofunc
:
function closure(){
var i = 1;
i++
var ofunc = function(j){return i+j;};
return ofunc;
}
We can now call the linked function like:
var x = closure()
x(10)
=> 12
Besides, if we add arguments to closure
too, we are able to create a class of parametric ofunc
-functions, where the arguments of the closure are the parameters of ofunc
. For example, if ofunc
defines a logarithm, the arguments of the closure might be set as its base, each creating different base logarithm operations, like: log10 = closure(10)
So it seems to me that the ()
-form is not practically interesting. Instead, it will be preferable to audit/debug by obtaining the source of the function returned by the closure with external parameters evaluated. Unfortunately with:
x.toSource()
we only get the unevaluated form:
=> '(function (j){return i+j;})'
How can we get the actual value of i
after executing closure
(but before executing x()
)? That is:
=> '(function (j){return 2+j;})'
Upvotes: 2
Views: 142
Reputation: 44444
There's a small gap in understanding. I will try to explain what's going on here.
A javascript closure closes over/captures the reference to the variable. This is what you are experiencing in your examples.
function closure(){
var i=1;
i++
var ofunc = function(){
//JS Interpreter: Ohh, there's a variable called *i*, let me hold that reference.
return i;
};
ofunc(); //JS: Let me call that function.
//While executing: Hey, I have the reference to i, what's the value?
//Returns 2 here.
i++;
return ofunc;
}
closure()(); //JS: Let me call that function.
//While executing: Hey, I have the reference to i, what's the value?
//Value retriever(or something): The **latest** value is 3.
//Returns 3 here.
This is because, the closure is capturing the reference and not the value. If you need to capture the value, you need to somehow make the closure capture the state of the variable. One way has been described by @Oriol. Another way, is to wrap it inside another closure, so that its the parameters that is captured. An example:
function closure(){
var i=1;
i++
var ofunc = function(x){return function(){return x;};}(i);
i++;
return ofunc;
}
closure()(); //returns 2, not 3! :-)
Let's dissect that line: var ofunc = function(x){return function(){return x};}(i);
var ofunc = function(x){ // ** (i)
func = function(){
return x; //Reference to parameter passed in outer function.
};
}
result = ofunc(i); //is a function.
return result;
When you call ofunc()
, you pass a parameter to it. This parameter is represented as (i). In the function body, you are creating a new function. This function is another closure. What this does is it retains the reference to x
(which comes from the parameter). Since this x
is the state of the variable, what you are essentially doing is: "Creating a function that captures the reference to the parameter passed in outer function". The parameter passed is the state of the variable. Hence, you create a function that kinda holds the state of a variable when ofunc
is called.
For understanding the practical benefits, lets consider the following use case: Make an array of 10 functions, where each function at index i
, will take an input parameter and return the result when that input parameter is multiplied by i
.
Case #1: (Wrong way of doing it!)
result = [];
for (var i=0; i<10; i++) {
func = function(x) {
return x*i;
};
result.push(func);
}
result[5](6); //Invoke the i-th function, expected result: 30, Actual result: 60!
Case #2: (Correct way)
result = [];
for (var i=0; i<10; i++) {
func = function(i_) {
return function(x){ return x*i_; };
};
result.push(func(i));
}
result[5](6); //result: 30
Upvotes: 1
Reputation: 288300
Just copy your variable to a new one at the moment it has your desired value:
function closure(){
var i = 1;
i++;
var i2 = i; /* Now i2 has desired value, even if you change i */
var ofunc = function(j){return i2+j;};
i++;
return ofunc;
}
var x = closure()
x(5);
Another possibility: pass your desired i
as an argument to a self executing function which returns another function:
function closure(){
var i = 1;
i++;
var ofunc = (function(i){ return function(j){return i+j;}; })(i);
i++;
return ofunc;
}
var x = closure()
x(5);
Upvotes: 1