Access a copied integer variable in javascript anonymous method - javascript

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.

Related

Enquiry on Javascript Closure

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

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.

Is it bad to duplicate declaring JavaScript variables?

Is it bad to duplicate declaring JavaScript variables? For instance, given the below code, is one approach better than the other? If so, please explain why. Thank you
function func1() {
for (var i = 0; i < 100; i++) {
var myVar=123;
}
}
OR
function func2() {
var myVar;
for (var i = 0; i < 100; i++) {
myVar=123;
}
}
Actually, these code samples are equivalent and will probably compile to the same bytecode.
function func1() {
for (var i = 0; i < 100; i++) {
var myVar=123;
}
}
AND
function func2() {
var myVar;
for (var i = 0; i < 100; i++) {
myVar=123;
}
}
This is because of hoisting, and in fact you could also do:
function func3() {
var i, myVar;
for (i = 0; i < 100; i++) {
myVar=123;
}
}
Although you might save time with function func4() { var myVar=123; } ;)
These are functionally identical.
Javascript takes two passes through code. During the first pass variables are set up (amongst other things).
In your first version, during the first pass of the interpreter it will see that you declare a variable myVar and it will hoist the definition of the variable to the top of the scope (which is the function in this case because loops don't have their own scope in javascript).
Thus in javascript's second pass, when the code is executed (interpreted), everything will be identical.
So, in javascript all variables act as if they were declared at the top of the scope, regardless of where, or if, you declare them.
One of the potentially confusing things about javascript is that it has c like syntax and yet has some significant differences from other c-like languages. And this is one of those differences.
It's generally considered better to declare variables at the beginning of a piece of code (or function), than at random places. One exception being the for-loop.
function func2() {
var myVar; // try to declare all variables here
for (var i = 0; i < 100; i++) { // except for the for-loop variable
myVar = 123;
}
}
Why I'd do this: because other languages work this way as well, so it makes it easier to read. For JavaScript specificity it doesn't really matter since scope works weird in JavaScript.
Main argument for this way of writing the function: readability.

Changing a Variable Out of Scope?

Is there any way to change a variable while out of scope? I know in general, you cannot, but I'm wondering if there are any tricks or overrides. For example, is there any way to make the following work:
function blah(){
var a = 1
}
a = 2;
alert(blah());
EDIT (for clarification):
The hypothetical scenario would be modifying a variable that is used in a setInterval function which is also out of scope and in an un-editable previous javascript file. It's a pretty wacky scenario, but it's the one I intend to ask about.
No. No tricks or overrides. You have to plan to have both places be able to see the variable in the same scope.
The only trick I can think of regarding scope is using window in a browser to get to the global object. This can help you get to a "hidden" variable--one that's in scope but whose name has been overtaken by a local variable (or other variable closer in the scope chain).
Closures and classes can afford you some other tricks with scope, but none that allow you to override the scoping rules entirely.
i don't see why you would need to do that, if you need a variable that is accessible from the outside, just declare it on the outside.
now, if you are asking this just because you are trying to learn something, good for you.
var a = 0;
function blah() {
a = 1;
return a;
}
a = 2;
alert(blah());
You can return the value from the function, of course:
function blah() {
var a=1;
return a;
}
But I assume that's not quite what you had in mind. Because a function invocation creates a closure over local variables, it's not generally possible to modify the values once the closure is created.
Objects are somewhat different, because they're reference values.
function blah1(v) {
setInterval(function() {
console.log("blah1 "+v);
}, 500);
}
function blah2(v) {
setInterval(function() {
console.log("blah2 "+v.a);
}, 500);
}
var a = 1;
var b = {a: 1};
blah1(a);
blah2(b);
setInterval(function() {
a++;
}, 2000);
setInterval(function() {
b.a++;
}, 2000);
If you run this in an environment with a console object, you'll see that the value reported in blah2 changes after 2 seconds, but blah1 just goes on using the same value for v.
Functions can access variables declared outside their scope, if they are declared before the function itself:
var a = 0;
function blah() {
a = 1;
}
a = 2;
alert(blah());
Note that your use of var a inside the function declared a local variable named a; here, we omit the keyword as otherwise it would hide a as declared in the outer scope!
No, that will never work, but you could use a global:
var a;
function blah(){
a = 1
}
a = 2;
alert(blah());
or use a closure:
function bleh() {
var a;
function blah(){
a = 1
}
a = 2;
alert(blah());
}
or you could pass it around with a return (which behaves differently, but probably is what you want to do):
function blah(a){
a = 1
return a;
}
a = 2;
alert(blah(a));

Variable declaration warning in VS2008

The following code in VS2008 gives me a "variable is already defined" warning:
if (someVar) {
var a = 1;
}
else {
var a = 2;
}
The warning is given on the second var a = .... To cure this warning I have done:
var a;
if (someVar) {
a = 1;
}
else {
a = 2;
}
But is this the correct way to do it?
Thanks,
AJ
Yes, that is the correct way to do it. There is no block scope in javascript; there is only function scope and global scope.
you could also give each "block" functional scope using anonymous functions, although it's not very practical in this case:
if (someVar) {
(function () {
var a = 1;
})();
}
else {
(function () {
var a = 2;
})();
}
As a side note, this is also why for (var i = 0; ...) is discouraged in favor of var i; for (i = 0; ...), to avoid 2 consecutive loops in the same function both trying to declare the variable i
It depends on how you're using those variables afterwards.
If they relate to the same object, then it's the correct way.
If they relate to different objects, then I'd rename the variables, as this would prevent maintenance issues in the future.
Either way is perfectly valid and fine (both examples ensure that the variable is declared with the var keyword), but generally it's best practice to declare your vars at the top of the current code block, as in your second example.

Categories

Resources