5
for(var i = 0; i < 100; i++){
  var foo = 5;

}

It works ...but is this bad?

I know I can declare var foo outside, but why do that when I'm only going to use it in the loop?

Alex
  • 66,732
  • 177
  • 439
  • 641
  • 11
    Actually, it's only declared once. All variable declarations are hoisted to the top of their scope (beginning of the function, or the global scope). Blocks do not create scope in JavaScript. – bfavaretto Aug 31 '13 at 01:03
  • @bfavaretto: At least not yet. – elclanrs Aug 31 '13 at 01:09
  • Even though the semantics act as bfavaretto described, I prefer to "keep the declarations close" to the usage site. This won't change how the code behaves, but *I find* that it makes the intent of the code more clear and I can scan code to see if something looks suspiciously out of place. Others may disagree, and that's fine. – user2246674 Aug 31 '13 at 01:09
  • It's hoisted, so it doesn't matter. Declaring it within the loop gives a visual indication that it is intended to be used only in the loop, but it is important to be aware that it is available outside the loop. – Andy G Aug 31 '13 at 01:10
  • user2246674: that's what I was thinking too! I was just wondering if it's considered a bad practice or smth, bc of the redundant `var`.. – Alex Aug 31 '13 at 01:11
  • @Alex *I prefer* to (and argue for) keep "declarations" close *and* limit one "declaration" per `var`, but many opinionated people and tools (i.e. JSLint) would disagree. Just be consistent and know the semantic rules: there is *no* performance or semantic difference. Here is a fun little quiz. What does `a = "outside"; function f () { a = "inside1"; return a; var a = "inside2" }; alert(a + " " + f())` result in? – user2246674 Aug 31 '13 at 01:12
  • @Alex The `var` is not redundant as without it the variable would be hoisted to the global scope, rather than to it's containing scope (perhaps a function). – Andy G Aug 31 '13 at 01:14
  • Oops, for better clarity, see: `a = "outside"; function f () { a = "inside1"; return a; var a = "inside2" }; alert(f() + " " + a)` (such that `f()` is evaluated before `a` is read). – user2246674 Aug 31 '13 at 01:18
  • @user2246674 Just as expected. – whirlwin Aug 31 '13 at 01:31
  • Unless in future you want to use `let` instead of `var` for creating block scope.. – PSL Aug 31 '13 at 01:39

2 Answers2

4

Over time my personal style has grown into a preference for declaring variables where they "actually are" in the "mind" of the particular language I am working in. For JavaScript this means putting variable and function declarations in the positions where the language would hoist them anyway.

This is for clarity, precise communication, understanding, maintainability, to keep my own mental processes parallel with the processes of the language, just to mention a few good reasons.

So in this particular case, I would not put a var foo = 5; declaration into the body of a for loop, for the simple reason that it is not doing what it looks like it is doing, i.e., it is not in fact redeclaring/re-scoping the variable with every iteration. (Placing the var declaration in the initialization portion of the for loop's header (initialization; condition; afterthought) would be more reasonable, and certainly the correct place to put it in languages that do have block-level variables, such as when JavaScript finishes adopting the let-style declaration for block-level scope.)

Notes:

  • This practice may not seem "normal." My mind prefers to keep variables "close" to their use, and my current practice does not come naturally to the way my mind works. But as a programmer through the years, experiences with confusion, bad communication, misunderstanding, unmaintainable code impossible to unravel, etc., have more than justified the extra discipline that it takes me to follow this style of declaring variables in the locations corresponding to their actual scope, regardless of the language.

  • One extra benefit of this practice for me has been that it encourages me to keep my functions modular and reasonable in size.

  • Along the same lines, following this practice automatically helps keep my code organized in a sensible way. For instance, if putting my functions first causes there to be excessive functions before the code I am actually working on, and I therefore get irritated about having to go down two pages to edit my program, following this style automatically gives me incentive to organize my code into an appropriate structure of distinct files.

P.S. One could bring up a point that some functions are so long that this practice makes variable declarations end up pages away from where they are used. Yes, I have seen code where that is true, and perhaps even code where that cannot be helped. Yet I can't help observing the parallel that is equally true for authors of prose as it is for computer programmers. As skill level grows, the lengths of authors' sentences (and sizes of programmers' functions) tend to grow, and then as the skill level continues to grow yet higher, the lengths of sentences (and sizes of programmer's functions) tend to shorten once more.

Joseph Myers
  • 6,434
  • 27
  • 36
  • Make sure you keep your methods slim, then you don't need that many variables in so many places. But I also prefer declaring the variables where they're used, even if they get hoisted. – Ruan Mendes Aug 31 '13 at 02:45
3

It is bad because it gives the false impression that i and foo are local variables.

for(var i = 0; i < 100; i++){
    var foo = 5;
}
foo; // 5

This might not be a problem with simple code, but if you use closures it becomes apparent that there is only one foo:

var counters = [];
for (var i = 0; i < 100; i++) {
    var foo = 5;
    counters.push(function() { foo++; return foo; });
}
counters[0](); // 6
counters[0](); // 7
counters[1](); // 8 (!)

To create foo in a different scope a function needs to be introduced:

var counters = [];
for (var i = 0; i < 100; i++) {
    (function() {
        var foo = 5;
        counters.push(function() { foo++; return foo; });
    })();
}
counters[0](); // 6
counters[0](); // 7
counters[1](); // 6

Here is a real-life example of things going wrong due to this: setTimeout in for-loop does not print consecutive values

Since JavaScript 1.7 you can use the let keyword (see MDN) to create true local variables.

Community
  • 1
  • 1
tom
  • 21,844
  • 6
  • 43
  • 36