Closure error with loops in javascript - javascript

I had a bug, it went like this
for(var i=0; i<arr.length; i++){
var v = arr[i]
var obj=new Thing
obj.TheCallback = function(e) { blah = v; domorestuff(); ... }
obj.runCodeToExecuteTheCallback()
}
The problem is v inside the function is using the v from the last loop. It's a closure thing where you can access variables in your parent scope. But my question is...
Why is javascript reusing v in each iteration? When it goes out of scope (at the end of the loop) i dont except it to be MODIFIED from any other scope unless it was passed in (such as the inner function). Why the heck is javascript clobbering my variable? Is there some kind of reason for this design? Is it a bug that has been decided never to be fixed?

This is a very common issue people encounter.
JavaScript doesn't have block scope, just function scope. So each function you create in the loop is created in the same variable scope, so they're referencing the same v variable.
To create a new scope, you need to invoke a function, and pass in whatever you want to exist in a new scope.
function createCallback( x ) {
return function(e) { blah = x; domorestuff(); ... }
}
for(var i=0; i<arr.length; i++){
var v = arr[v]
var obj=new Thing
obj.TheCallback = createCallback( v );
obj.runCodeToExecuteTheCallback()
}
Here I invoked the createCallback() function, pass in v, and had createCallback() return a function that references the local variable (named x in the function, though you could name it v as well).
The returned function is of course assigned to obj.TheCallback.
Its variable scope exists as long as the function exists, so it will continue to have reference to any variables that it needs that were in the variable scope when it was created.

Another solution:
var i, obj;
for ( i = 0; i < arr.length; i++ ) {
obj = new Thing;
(function ( v ) {
obj.callback = function ( e ) {
// do stuff with v
};
})( arr[i] );
}

Related

In this code, why I have to make outer function?

var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function(id) {
return function() {
return id;
}
}(i);
}
for (var index in arr) {
console.log(arr[index]());
}
My thinking:
'I' which is in the (i); will refer to a global variable 'I'.
'I' which is in the (i); will be saved in the 'id' which is in the function(id).
'id' is a local variable of outer function. 'id' which is in the inner function will refer to 'id' which is a local variable of outer function. Then, the result is '0,1,2,3,4'.
But I think I can get the same result without outer function.
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function(id) {
return id;
}(i);
}
for (var index in arr) {
console.log(arr[index]());
}
In this case, 'I' which is in the (i); will refer to 'I' which is a global variable.
'I' which is in the (i); will be saved in 'id' which is a local variable of function.
'id' which is behind return code will refer to 'id' which is a local variable of function. Then, the result is '0,1,2,3,4'.
Using outer function like the first code is unnecessary because the second code is possible. Am I right?
You could even not use any function at all, and just set arr[i] = i. But something tells me that you've simplified the supplied code in such a way that the reason for using such a pattern was removed.
This kind of function pattern is called partial function application, where you apply some parameters to a function and get back another function of smaller arity.
In such a case as what you've shown, one benefit of this approach is saving the execution of a result function to be called later, but applying the known parameters before that time.

Will javascript create a global x?

I was wondering in this example if x would become a global variable as if was not declared inside the local function? Will javascript exit the local function, search until it doesnt find an x, and then implicitly create a global x?
function f(){
var ar=[],i;
for(i=0;i<3;i++){
//this time instead of passing i, the funciton has a local value called
x
arr[i]=(function(x)){
return function(){
return x;
};
}(i));
}
return arr;
}
Try running the code below and notice how we added the () for function x. Take those out and notice the difference in the console.log. Your edited question is kinda dumb. Just console.log(x) and see what happens
var WINDOW_PROPS = Object.keys(window);
arr =[]
function f(){
for(i = 0; i < 3; i++) {
arr[i] = (function(x){
return function (){
return x*x;
}();
}(i));
console.log(arr[i])
}
var GLOBALS = Object.keys(window)
// filter the props which your code did not declare
.filter(prop => WINDOW_PROPS.indexOf(prop) < 0)
// prettify output a bit :) It's up to you...
.map(prop => `${typeof window[prop]} ${prop} ${window[prop]}`)
// sort by types and names to find easier what you need
.sort();
console.log(GLOBALS.join("\n"));
return arr;
}
f()

Understanding JavaScript Closures with a small example

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.

How do I prevent an argument to a javascript function being retained by a closure?

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.

Access a copied integer variable in javascript anonymous method

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.

Categories

Resources