2

I ran into this little gem 5 minutes ago. I have been playing with JavaScript for a long while now and, since I follow best practice, I've never met such case, nor understand why it works while I supposed it shouldn't :

for (var i=0; i<10; i++){
   // ... something
}
console.log("i=", i);

will output 10

How is i available outside the for block? I always thought the declaration part was to have a local variable only available within that block.

Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214

5 Answers5

4

I always thought the declaration part was to have a local variable only available within that block.

Nope, not in JavaScript.

JavaScript loops (and most blocks in general) have no block scoping (until then next version rolls out with let.)

There are only two places where JavaScript does block scope at the moment and that's with clauses (you shouldn't use those anyway) and catch clauses.

Instead, JavaScript relies mostly on function scoping - variables declared in a function are local to that function.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • This answer is more or less saying the same as the suggested "duplicate" question. (Sorry about the repetition. Blame Google for not being smart enough when querying all sorts of things on the subject.) – Yanick Rochon Dec 06 '13 at 02:41
  • 2
    @YanickRochon for future reference, this is referred to as "Hoisting" the variable. See [MDN article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var) on `var` – jasonscript Dec 06 '13 at 02:51
  • @jasonscript, first time I see this term :) (Not native english.) – Yanick Rochon Dec 06 '13 at 03:01
  • @YanickRochon Hoisting pretty much means "lifting". So the var declarations are lifted to the top of the function block automatically. – jasonscript Dec 06 '13 at 03:05
2

Because that's equivalent to:

var i=0;
while (i<10){
   // ... something
   i++;
}

In fact, loops do not even create their own scope at all:

var x = 0;
while (x < 10) {
  x++;
  var i = 5;
}
i; // 5
tckmn
  • 57,719
  • 27
  • 114
  • 156
  • Interesting. Like I said, I've never wrote such... crappy code, so I would have never guessed without actually trying it. – Yanick Rochon Dec 06 '13 at 02:31
  • @BenjaminGruenbaum Yes, I suppose so; I'll just remove the spec reference then :P – tckmn Dec 06 '13 at 02:35
  • @Doorknob these are the droids you're looking for http://es5.github.io/#x10.2 :) – Benjamin Gruenbaum Dec 06 '13 at 02:36
  • @BenjaminGruenbaum That still doesn't really list what creates a new scope and what doesn't though – tckmn Dec 06 '13 at 02:37
  • @Doorknob no it doesn't and the list is a lot more exhaustive than one might think, it's not just `with`, `catch`, FunctionExpression, FunctionDeclaration and global scope (although those are the major ones). Those are the ones if you avoid eval (direct or indirect) scope for the most part (not to mention host object extensions). That's the reason for the usual incorrect but good enough "JS only has only function scope" :) – Benjamin Gruenbaum Dec 06 '13 at 02:41
2

In this case the declaration of i is outside the code block. In any case, Javascript doesn't have a block-level scope. Variables are either global, or within the scope of a function.

0

i will be available below where you have defined it, unless you close off the scope, like:

(function(){
  for(var i=0; i<10; i++){
  }
})();
console.log(i); // undefined

This should not matter though, because as long as you are using other loops below you can use the same increment variable (with the exception of nested loops), which will overwrite the other one. A problem could arise when you have a loop where you forget to use the keyword var. Always use var would be my recommendation. A closure is not usually necessary.

In a nested loop you can use the same increment variables again like:

for(var i=0; i<10; i++){
  for(var n=2; n<44; n+=2){
  }
}
// feel free to use `i` and `n` again

Or loops like this:

var ar1 = ['a', 'b', 'c']; 
for(var i=0,l=ar1.length,n=0; i<l; i++,n+=2){
}
// feel free to use `i`, `l`, and `n` again

Personally, I find it a best practice to reserve vars i, n, c, and q for counters and l for length. Then I don't use them elsewhere, besides in loops.

StackSlave
  • 10,613
  • 2
  • 18
  • 35
  • I'm sorry, but this does not address the question directly. The question is not about finding ways to isolate `i`, but why it is visible outside the `for` block. – Yanick Rochon Dec 06 '13 at 02:43
0

In javascript, there's no "block scope", but "function scope". It means that, as soon as your variables are defined inside a function, they'll remain alive from the point that they're declared until the end of that function, whether inside or out side a block.

Here is the test case for js variable scope (from Secrets of the Javascript Ninja, sections 3.2.1 Scoping and functions)

Test case:

function outer(){
  var a = 1;
  function inner(){ /* does nothing */ }
  var b = 2;
  if (a == 1) {
    var c = 3;
  }
}
outer();

Result:

enter image description here

Hoa Nguyen
  • 13,452
  • 11
  • 45
  • 44