I have some javascript code that resembles this:
for (i = 0; i < numTimes; i++) {
DoStuff();
}
function DoStuff() {
for (i = 0; i < 100; i++) {
console.log(i);
}
}
I am finding that the second time the DoStuff() is called, the value of i in the loop starts with 1. I assume this is due to the way the scoping of variables work in JS. Other than changing the variable name in the DoStuff() function, what's the cleanest way of resolving this and can someone explain this behavior?
EDIT: Thanks for the responses. It appears that JS has "lexical scope" instead of "block scope". Is this what I am seeing here? Can someone explain what lexical scope is in newbie terms?
for (var i = 0; i < numTimes; i++) {
DoStuff();
}
function DoStuff() {
for (var i = 0; i < 100; i++) {
console.log(i);
}
}
In javascript, any variable that isn't first declared with the var keyword is global. Adding the var keyword makes it function-local, that is local to the function. Javascript doesn't have block scoping, so if for instance you declared a variable with var inside and if block, it would not be local to the if block, it would be local to the function that contains it.
Use the var keyword. This will limit the scope of i
for (var i = 0; i < 100; i++)
Put a var in front of the variable inside the for loop:
for (var i = 0; i < 3; i++) {
console.log(i);
}
for (var i = 0; i < numTimes; i++) {
DoStuff();
}
function DoStuff() {
for (var i = 0; i < 100; i++) {
console.log(i);
}
}
You should declare iterator variable with "var". If you do not, then you declare global scope variable
your i variable is being implicitly set at a global level, meaning it is accessible (and modifiable!) to any script, anywhere. This is a bad thing, as you have just discovered. The solution is to use the var keyword, which limits the variable to the nearest enclosing function:
for(var i=0; i<100; i++){
To elaborate on a previous answer,
changing i = 0 to var i = 0 will give you the behavior you're looking for.
The reason for this is that if you declare without the var you are declaring it as a global variable, whereas declaring with var makes it local within the scope of the function it is defined in. It should be also be noted that variables declared outside a function, with or without var will be global.
More info here
If you don't declare your variable (i.e. using "var i;" or "var i=0;"), it is created as a global variable, and its scope is the whole program. (THIS IS VERY BAD!).
Note also that JavaScript does not have block scope, and so if you declare your variable in the for loop, it still has scope for the entire function.
Change i = 0; to var i = 0; Example:
for (var i = 0; i < numTimes; i++) {
DoStuff();
}
function DoStuff() {
for (var i = 0; i < 100; i++) {
console.log(i);
}
}
Related
I have read Variables and scoping in ECMAScript 6, but I still got some strange output when I execute the codes in browser console below
for(let i = 0; i < 0;) {
var i
}//Uncaught SyntaxError: Identifier 'i' has already been declared
for(let i = 0; i < 0;) {
let i
}//undefined
Why the first one throws an error?
When I try this in parameter, it goes opposite
function foo(x){
let x
}//Uncaught SyntaxError: Identifier 'x' has already been declared
function foo(x){
var x
}//undefined
The first sample throws because
for(let i = 0; i < 0;) {
var i
}
is equivalent to the following code due to hoisting of var
var i
for(let i = 0; i < 0;) { // <-- throws because `i` has already defined
}
The second sample wont throw because loop body never executes and even if it would for-loop body creates a block scope so it is ok to have another let i in it
for(let i = 0; i < 0;) {
throw new Error('Never reached')
let i
}
for(let i = 0; i < 2; i++) {
let i = 'bar' // ok
console.log(i)
}
The third sample throws because name x already binded to an agrument.
function foo(x){
let x // throws because `x` already used
}
The last sample is ok because var can redefine names
function foo(x){
var x
var x
var x
}
The difference is scoping. var is scoped to the nearest function block and let is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block.
Also, variables declared with let are not accessible before they are declared in their enclosing block. As seen in the demo, this will throw a ReferenceError exception.
if you want more explanation you can read the following answer with some examples for better understanding,
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.
If I do:
var i = j = 0;
Is j a local variable?
Prove it.
After hoisting, your code looks like:
var i;
j = 0;
i = j;
Therefore i is a local variable, but j is not.
j would be a global variable, or get assigned to a variable in an out scope:
(function() { var i = j = 0; })()
// i is undefined
// j is 0
var i = 42;
var j = 1;
(function() { var i = j = 0; })()
// i remains 42
// j is 0
For fun, here is another "proof":
(function() {"use strict"; var i = j = 0;}());
// throws "ReferenceError: assignment to undeclared variable j"
(Read more about strict mode)
Since the declaration of j isn't in the same declaration expression as i, the variable is either created in the outer scope or, if it exists there it will overwrite the value in the outer scope.
The reason why i is now global is because of variable hoisting. You can break this down like this:
1) var i
2) j, which now declares j in the current scope, which is the containing scope since the expression is not bound to the current context because it's not using var.
3) = 0, which now assigns j to 0, and subsequently assigns j to i.
Proof?
(function(){
var i = j = 0;
})();
try{
alert(i);
}catch(e){
alert(e);
}
alert(j);
http://jsfiddle.net/kDGM3/1/
Not particularly proof...nor would I use an exception in a normal program flow unless it was indeed an exceptional case. But it does demonstrate the behavior.
function test(a){
var i=j=0;
return i;
};
test(100);
alert(j);
alert(i);
check if it's on the window object alert(window.j) if it alerts it's value then it's global if not it's local (if it's not in a function when using the var keyword then it's global and without var then it's global no matter were you define it. so j and i are both global). example:
var i = j = 0;
function x(){
var r = 100;
alert(r);
}
alert(window.i); //will alert 0.
alert(window.j); //will alert 0.
x(); // will alert 100.
alert(window.r); //will alert undefined.
or you can use hasOwnProperty like so alert(window.hasOwnProperty("i")) which returns a boolean value.
by the way, trying to test this using jsfiddle will make i return undefined (might have something to do with the way jsfiddle protects it's own global namespace) so you'll need a blank html page to test this
for (var i in variables) {
eval('var ' + i + ' = variables[i]');
}
Basically, I want to transfer variables properties to local variables.
Is there an alternative to using eval()
Or which of these is better:
1.
var _ = variables;
for (var i = 0; i < 100000; i++) {
_.test1();
_.test2();
_.test3();
}
2.
with (variables) {
for (var i = 0; i < 100000; i++) {
test1();
test2();
test3();
}
}
3.
var test1 = variables.test1,
test2 = variables.test2,
test3 = variables.test3;
for (var i = 0; i < 100000; i++) {
test1();
test2();
test3();
}
4.
for (var i in variables) eval('var ' + i + ' = variables[i]');
for (var i = 0; i < 100000; i++) {
test1();
test2();
test3();
}
By looking at your comment seems that your major concern is having to reference several times a deeply nested object, avoiding eval and with I would simply recommend you to use an alias identifier, for example:
// some local scope...
var foo = namespace.constructors.blah.dramatic.variables;
// replace foo with something meaningful :)
foo.method1();
foo.method2();
foo.property1;
// etc...
In that way the deeply nested object will already be resolved and referencing your alias will be faster, eval and with IMO would only cause you more problems than benefits in this case.
One alternative is to make the object the current scope. It won't make the properties local variables, but you can access them using the this keyword:
var variables = { a:42, b:1337 };
(function(){
alert(this.a);
alert(this.b);
}).apply(variables);
This has the advantage that you are not copying anything anywhere, you are accessing the properties directly.
Well, I'll post the answer myself.
No.
There is no way to set local variables without knowing the name of the variable without using eval()...
...But using local variables (option 3) is the best way.
Check out the following snippet of HTML/Javascript code:
<html>
<head>
<script type="text/javascript">
var alerts = [];
for(var i = 0; i < 3; i++) {
alerts.push(function() { document.write(i + ', '); });
}
for (var j = 0; j < 3; j++) {
(alerts[j])();
}
for (var i = 0; i < 3; i++) {
(alerts[i])();
}
</script>
</head><body></body></html>
This outputs:
3, 3, 3, 0, 1, 2
which isn't what I was expecting - I was expecting the output 0, 1, 2, 0, 1, 2,
I (incorrectly) assumed that the anonymous function being pushed into the array would behave as a closure, capturing the value of i that's assigned when the function is created - but it actually appears that i is behaving as a global variable.
Can anyone explain what's happening to the scope of i in this code example, and why the anonymous function isn't capturing its value?
The scope is the function that the variable is defined in (except there isn't one, so it is global).
The anonymous function you are passing is accessing the variable defined in the parent function's (again global) scope.
You need an actual closure.
alerts.push(
function (foo) {
return function() {
document.write(foo + ', ');
}
}(i)
);
In Javasript, the only "interesting" lexical scope boundary is the function body. Anything declared anywhere in a function (well, anywhere other than another nested function!) is at the same scope. There are also some weird things about the way that the declarations are interpreted.
Your anonymous function does act as a closure, but each function instantiated will share the same "i". A trick I use is to add another layer of function:
for (var i = 0; i < whatever; i++) {
(function(idaho) {
whatever(function() { alert("my own private " + idaho); });
})(i);
}
At somepoint hopefully all the browsers will support the new "let" statement, which is a shorter, less weird-looking way to do basically the same thing.