I came across a JS Function in Effective Javascript book by David Herman. I have copied the code below. I just want to know how Updated Value of "i" is accissible in hasNext function, when "i" is incremented only in next function.
Javascript
function values() {
var i = 0, n = arguments.length; a = arguments;
return {
hasNext: function() {
alert(i);
return i < n;
},
next: function() {
if (i >= n) {
throw new Error("end of iteration");
}
return a[i++];
}
};
}
var it = values(1, 4, 1, 4, 2);
JsFiddle:
http://jsfiddle.net/G42Ev/
i is declared on line 2 (via var). It is inside values and thus scoped to values.
Both next and hasNext are declared (by virtue of being function expressions) inside values (and don't mask i by declaring another i in a narrower scope) so have any access to any variables scoped to it.
When i is accessed by either next or hasNext, they don't find an i in their own closure, so they look up one closure and find an i in the closure of values (the closure in which var i happens)
This means changes to and reads from i in those two functions both share the same i
Related
I am trying to get around understanding javascript closures from a practical scenario.I know from a theoretical perspective , With the help of closures inner functions can have access to the variables in the enclosing function i.e parent function.
I have read a couple of questions on stackOverflow as well.
i am really missing the point of what is happening here?
var foo = [];
for(var i=0;i<10;i++){
foo[i] = function(){
return i;
}
}
console.log(foo[0]());
This gives me out a 10. Most of the articles say that by the time it reaches the inner anonymous function, The for loop is getting executed as a result the last value that is present in the loop which is 10 is being printed.
But i am still not able to get to the bottom of this.
On Contrary, If i use something like:
var foo = [];
for(var i=0;i<10;i++){
(function(){
var y =i;
foo[i] = function(){
return y;
}
})();
}
console.log(foo[0]());
I am getting the output.Any help would be highly appreciated.
maybe this code block helps
var foo = [];
for(var i = 0; i < 10; i++) {
foo[i] = function() {
return i; // is a reference and will always be the value, which 'i' have on function execution
}
}
// 'i' is 10 here!
console.log(foo[0]()); // executing the function will return the current value of 'i'
///////////////////////////////////////
var foo = [];
for(var i=0;i<10;i++) {
/* thats a IIFE (immediately invoked function expression) */
(function(a) { // 'a' is now a local variable
foo[a] = function() { // defines a function
return a; // is a reference to local variable 'a'
};
})(i); // <- passing the current value of i as parameter to the invoked function
}
// 'i' is 10 here
console.log(foo[0]()); // returns the reference to 'a' within the same scope, where the function was defined
In your first scenario, all of your functions added to the foo array are referencing the same var i. All functions will return whatever i was set to last, which is 10 because during the last iteration of the loop that's what it's value was set to.
In the second scenario, you are Immediately Invoking this function:
(function(){
var y =i;
foo[i] = function(){
return y;
}
})();
By immediately invoking it you are effectively locking in the local state of var y, for each iteration of the loop - it provides a unique scope for each function added to the array.
If I define an inner function inside a function, the inner function has access to the outer function's variables. If I want this inner function to be reusable and define it outside the outer function, the inner function now loses access to the outer function variables. How do I make this new reusable inner function have access to outside function variables, without passing those variables in as parameters?
function a () {
var x = [[1,2,3], [1,2,3], [1,2,3]];
var keys = Object.keys(x[0]);
for (var i = 0; i < x.length; i++) {
angular.forEach(keys, loop);
}
}
function loop (key) {
console.log(key, i);//i is undefined here
}
a();
Specifically, is there some way without 1) assigning variables to this, 2) without passing in variables as parameters, and 3) without creating global variables?
Edit: It seems there is no way to do this. But if I try another approach, to have the reusable function return a new function, I also do not have access to the inner scope. Why is this, and is there some way to make this work?
function a () {
var x = [[1,2,3], [1,2,3], [1,2,3]];
var keys = Object.keys(x[0]);
var myloop = loop();
for (var i = 0; i < x.length; i++) {
angular.forEach(keys, myloop);
}
}
function loop (key) {
return function(key) {
console.log(key, i);//i is undefined here
};
}
a();
In the following example, loop returns a function that closes over the value of i.
function a () {
var x = [[1,2,3], [1,2,3], [1,2,3]];
var keys = Object.keys(x[0]);
for (var i = 0; i < keys.length; i++) {
keys.forEach(loop(i));
}
}
function loop (i) {
return function (key) {
console.log(key, i); // i is now defined
}
}
a();
Output:
0 0
1 0
2 0
0 1
1 1
2 1
0 2
1 2
2 2
How do I make this new reusable inner function have access to outside function variables, without passing those variables in as parameters?
You can't. JavaScript has lexical scope, not dynamic scope.
See also: What is lexical scope?
I also want to make another option known which I just discovered. If you use .bind, you can curry the function with i, and the other variables will be passed in after the curried parameters.
....
angular.forEach(keys, loop.bind(null, i));
...
function loop(i, key) {
...
}
Inner functions are treated locally by the outer function. Therefore, you can access the variables belonging to the outer function from the inner function. But, once you have the inner function as a separate function outside the outer function, then you no longer have access to the private data variables of the outer function.
If this seems complicated, here is an example:
function A
{
//variables
function B
{
can use variables of A
}
}
But,
function A
{
//variables
}
function B
{
//cannot access variables of A
}
In JS (and many other languages), there is a visibility context. Possible contexts are e.g. "global" or function or block. These contexts are hierarchical, inner can read outer. Outer can never read inner (encapsulation principle) unless inner declares variable as global.
I have a for loop where I get the start number for my countdown. I want to use that number outside the for loop and here comes the closure problem.
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //the number is 10
getNumber(number);
};
So I thought I should call a function that returns that number so I can use it somewhere else:
function getNumber(number) {
return number;
}
But when I try to do this, I get an undefined instead of 10:
var globalVariableForNumber = getNumber();
What I know I am doing wrong is calling getNumber() without the parameter when assigning the value to my variable, but how else should I do it?
The number comes from an ajax call that has more numbers in it (response[i].number). I then want to use those numbers to be the start timers of my countdown. So if the number is 10, then my countdown will start from 10.
Thank you.
var response = [
{number:5},
{number:6},
{number:7},
{number:8},
{number:9},
{number:10}
]
var number;
for (var i = 0; i < response.length; i++) {
number = response[i].number; //the number is 10
console.log(getNumber());
};
function getNumber() {
return number;
}
// try it again later...
console.log(getNumber());
Again, you should just be calling number directly. But for the purpose of this question you have to declare number in a higher scope.
Scope
The current context of execution. The context in which values and
expressions are "visible," or can be referenced. If a variable or
other expression is not "in the current scope," then it is unavailable
for use. Scopes can also be layered in a hierarchy, so that child
scopes have access to parent scopes, but not vice versa.
A function serves as a closure in JavaScript, and thus creates a
scope, so that (for example) a variable defined exclusively within the
function cannot be accessed from outside the function or within other
functions.
Consider this example of scopes that your code could be declared and called:
function ImInGlobalScope(){ //Function declared in global scope
//Lets call this block 1
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //Inside function scope
getNumber(number)
};
//Lets call this block 2
function getNumber(number){ //In same scope than for statement
return number; //block 3
}
}
var globalVariableForNumber = getNumber();
With this example getNumber is undefined because it belongs to ImInGlobalScope() scope. Consider another scenario of scopes for your code.
function ImInGlobalScope(){ //Function declared in global scope
//Lets call this block 1
for (var i = 0; i < response.length; i++) {
var number = response[i].number; //Inside function scope
getNumber(number)
};
}
//Lets call this block 2
function getNumber(number){ //In same scope than for statement
return number; //block 3
}
var globalVariableForNumber = getNumber();
I believe the above is what your scenario is: getNumber is on global scope but number is ImInGlobalScope().
So when you're calling var globalVariableForNumber = getNumber(); We have the following:
function getNumber(number){ //Number is not being passed so is undefined
return number; //no variable named number exists in this scope so it will return undefined.
}
What you are doing is creating a function that takes exactly one parameter and returns that parameter when it is called. Return is a fancy way of saying that if you say foo=bar() then foo is whatever bar() returned.
In your code, calling getNumber() with no parameter returns undefined because it just returns the parameter. What you should do instead is not return the parameter, but instead set the global variable to it like so:
function getNumber(number) {
globalVariableForNumber = number;
}
Now, just get the number by running:
globalVariableForNumber
EDIT: The context for this question is, the argument is the source string for a module being loaded which could be huge, and there are many many modules being loaded this way, and each one has a closure with the original source code of the module held within it, using memory, when its not needed or wanted.
Specifically, I am trying to fix this https://github.com/dojo/dojo/blob/master/dojo.js#L367 code which leaks the module source into the closure.
<script>
function closureTest(n) {
function eval_(__text) {
return eval(__text);
}
for (var i = 0; i < n; i++) {
var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
m(5);
}
}
</script>
<button onclick="closureTest(1000)">Run</button>
In the above code, if a breakpoint is placed inside the anonymous function, and the closures that exist are examined, we can see that one of the closures contains __text and arguments[0] that contains the original source code of the module as it was passed to eval_
Here is a variation of the above:
<script>
function closureTest(n) {
function eval_() {
return eval(arguments[0]);
}
for (var i = 0; i < n; i++) {
var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
m(5);
}
}
</script>
<button onclick="closureTest(1000)">Run</button>
In this case, the closure no longer contains __text but does still contain arguments[0] with the string passed to eval_.
About the best I could come up with is the following, which deletes the argument to eval_ after processing it, a side effect is that the module being defined now also appears in the closure as a variable called module.
<script>
function closureTest(n) {
function eval_() {
var module = eval(arguments[0]);
delete arguments[0];
return module;
}
for (var i = 0; i < n; i++) {
var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
m(5);
}
}
</script>
<button onclick="closureTest(1000)">Run</button>
Is there a better way to prevent the closure retaining a copy of the argument passed to eval_?
[EDIT] Completely missed the point of the question.
Short answer: You cannot. As you are aware, a closure maintains the environment state from which it was created; there's no way to "break out" of a closure scope hierarchy.
When you call m() you are not calling it within a global context, but rather part of a chain of environments of, not only the eval_ closure but also closureTest as well -- You can see your returned eval'ed function also has access to the i and n vars defined in closureTest.
So, you cannot restrict or break out of a closure. However, if you define a new variable in your new scope with the same name as a variable from a previous closure you lose direct access to that. Therfore, your middle example is as close as you can get:
function closureTest(n) {
function eval_() {
return eval(arguments[0]);
}
for (var i = 0; i < n; i++) {
var m = eval_("(function(){ var x = 10; return function(n) { return x+n; }; })(window);");
m(5);
}
}
Here, your returned eval'ed function cannot access eval_'s arguments since it's own arguments have overridden it (it does, however, still have access to i and n from the for loop). This means, functionally, that eval'ed string cannot access the arguments passed to eval_.
Notes:
Yes, when you set a breakpoint in DevTools you can inspect the parent closures however that does not impact the fact that the executing function cannot directly access eval_'s `arguments[0].
Also, the function could get the string of the upper functions using arguments.callee.caller.toString(); etc. This still doesn't allow the function direct access to redefined vars, but thought it was worth mentioning.
I am a C# developer and used to the way closures work in C#.
Currently I have to work with anonymous javascript functions and experience a problem with the following snippet:
function ClosureTest() {
var funcArray = new Array();
var i = 0;
while (i < 2) {
var contextCopy = i;
funcArray[i] = function() { alert(contextCopy); return false; };
i++;
}
funcArray[0]();
funcArray[1]();
}
I expect the first funcArray() call to say 0 and the second to say 1. However, they both say 1. How is that possible?
By writing var contextCopy = i I make sure that I create a copy of the i-variable. Then, in each while-iteration I create a completely new function pointer. Each function refers to its own copy of i, which is contextCopy. However, both created functions for some reason refer to the same contextCopy-variable.
How does this work in javascript?
JavaScript has lexical closures, not block closures. Even though you are assigning i to contextCopy, contextCopy is, itself, a lexical member of ClosureTest (which is different from C#, where the {} give you a new scoped block). Try this:
while (i < 2) {
funcArray[i] = (function(value) {
return function(){ alert(value); return false; }
})(i);
i++;
}
Curly braces ({}) in JavaScript do not capture variables as they do in C#.
Only closures (functions) introduce new scope, and capture variables.
var i = 0;
while (i < 2) {
var contextCopy = i;
...
}
is actually interpreted as:
var i, contextCopy;
i = 0;
while (i < 2) {
contextCopy = i;
...
}
To get a copy of the variable, you'll need to wrap the code with a closure:
var i;
i = 0;
while (i < 2) {
(function (contextCopy) {
...
}(i));
}
You don't create a copy of the i variable. Instead, you make this variable GC-dependant of the closures that use it. It means that when the while loop exits, the i variable continues to live in its last state (1) and both closures reference to it.
Another way to put it: closing over a variable does not copy it into your closure (would make little sense for objects), it just makes your closure reference the variable and ensures this variable is not GCed untill the closure is.