I was reading a blog post here about creating a scraper with node.js
and came across an interesting bit of javascript that I can't quite wrap my head around. This is the exact sort of thing I would like to use in my scripts, but as a bit of a newbie I don't want to blindly copy and paste code without knowing exactly what they do first.
In this function:
function main()
{
var a = 1;
var f = function() { console.log(a); }
a = 2;
f();
}
main();
the output is 2
, because var a
is changed before f()
is called.
However, in this function
function main()
{
var a = 1;
var f = ( function(a) { return function() { console.log(a); } } )(a);
a = 2;
f();
}
main();
the output is 1
. There is a fairly detailed explanation of this in the blog post linked above, but I can't for the life of me work out exactly why this works.
The post mentions the scope of var a
being passed into the function - can anyone elaborate on what this means? And why is it necessary to have the final (a)
at the end of the var f
function?
I was reading a blog post here about creating a scraper with node.js
and came across an interesting bit of javascript that I can't quite wrap my head around. This is the exact sort of thing I would like to use in my scripts, but as a bit of a newbie I don't want to blindly copy and paste code without knowing exactly what they do first.
In this function:
function main()
{
var a = 1;
var f = function() { console.log(a); }
a = 2;
f();
}
main();
the output is 2
, because var a
is changed before f()
is called.
However, in this function
function main()
{
var a = 1;
var f = ( function(a) { return function() { console.log(a); } } )(a);
a = 2;
f();
}
main();
the output is 1
. There is a fairly detailed explanation of this in the blog post linked above, but I can't for the life of me work out exactly why this works.
The post mentions the scope of var a
being passed into the function - can anyone elaborate on what this means? And why is it necessary to have the final (a)
at the end of the var f
function?
-
This is a function expression, which gets immediately invoked by
... (a)
which also passes ` a` to the function (think of a simple function call). Where the value ofa
gets preserved because it's now a local var of the function expressions scope. and variables get passed by value – Moritz Roessler Commented Jun 4, 2013 at 6:07
5 Answers
Reset to default 5Maybe you can understand it if it is written as follow
function main()
{
var a = 1;
var f = ( function(b) { return function() { console.log(b); } } )(a);
a = 2;
f();
}
main();
This is called variable shadowing
- by naming the parameter of the function with the same name we are hiding it from the function scope. If we do not shadow the variable, as in my example, the code is straighforward - we are defining function (1) that returns function (2), the returned function upon execution prints the value passed to function (1). we are passing the current value of a to the function, so the resulting code is
var f = ( function(b) { return function() { console.log(b); } } )(1);
or
var f = function() { console.log(1); }
In JavaScript, variables inside a function can be accessed in two ways:
When they're part of the execution context (scope) where the function is declared.
When they're passed as an argument when the function is called.
In the latter case, the function argument shadows any variable from the scope bearing the same name and therefore changes to that variable outside of the function don't have any effect on the one inside.
In your second code example, the inner function is assigned to f
via immediate invocation of the outer function, with the added bonus of having a consistent value of a
.
Note, however, that scalar values are easier to shadow than objects; consider this code:
var a = { x: 1 },
f = (function(a) {
return function() {
console.log(a);
};
}(a));
a.x = 123;
f(); // Object { x: 123 }
Even though the value a
was preserved, it's still an object and therefore any changes to its properties are still seen inside f()
. That said, observe what happens when a
itself is changed instead of its properties:
a = { x: 456 };
f(); // Object { x: 456 }
This time, the previous value of a
is retained and the variable outside of the function is being assigned a new value.
Sure, so the scope of the variable is sort of the environment that the variable can be affected by or can affect. So this is returning a closure, basically a snapshot of the scope or environment of that function. Think of it as you are encapsulating everything in that closure and nothing outside of the function that is being returned can affect what is inside it. Now the variables scope 'a' in this case is just that one function. Closures can be a very powerful tool, and are one of the reasons I really like JavaScript.
So you have this function(clearest this way):
var f = (function(a) {
return function() {
console.log(a); }
}
)(a);
Wrapping the function insde parenthesis makes the function directly executed, which means that the function will be executed after assigning 1 to a
, but before assigning 2.
The (a)
after the wrap is the parameter passed to the function.
So the value passed to the function is a=1
Inside this function, you return another function which will log the value of a
(the parameter), so 1.
By assigning this to a variable f, you keep the state of the variables inside the function at the moment it has been executed, ie before assigning a=2
.
function main()
{
var f = ( function(a) { return function() { console.log(a); } } )(a);
a = 2;
f();
}
main();
var a = 1;
// A simple variable is added..
var f = ( function(a) { return function() { console.log(a); } } )(a);
Here f
is self executing function with a as input.
As it is executed, now you are returning a function, which gets a
as input variable. Here you have to understand that you are sending the copy of a, not a. This means you are extending the scope of a
to be used even after the function execution. But according to the f
it was passed with some parameter and it does not know a
is changed outside because you are passing the copy of a
, not a
. So it extends the scope of copy of a
This is how it works. And please see the topic "Currying" in JavaScript to understand this better.