12

Before asking my question, let me give a disclaimer. I know what var does, I know about block scope, and I know about variable hoisting. I'm not looking for answers on those topics.

I'm simply wondering if there is a functional, memory, or performance cost to using a variable declaration on the same variable more than once within a function.

Here is an example:

function foo() {
  var i = 0;
  while (i++ < 10) {
    var j = i * i;
  }
}

The previous could just have easily been written with the j variabled declared at the top:

function foo() {
  var i = 0, j;
  while (i++ < 10) {
    j = i * i;
  }
}

I'm wondering if there is any actual difference between these two methods. In other words, does the var keyword do anything other than establish scope?

Reasons I've heard to prefer the second method:

  1. The first method gives the appearance of block scope when it's actually function scoped.
  2. Variable declarations are hoisted to the top of the scope, so that's where they should be defined.

I consider these reasons to be good but primarily stylistic. Are there other reasons that have more to do with functionality, memory allocation, performance, etc.?

Philip Walton
  • 29,693
  • 16
  • 60
  • 84
  • I doubt the 4 bytes for the "var " vs. the two bytes for ", " aren't really significant. – Lee Meador Mar 26 '13 at 21:16
  • @LeeMeador I'd disagree. It's only 4 bytes if it's UTF-8 or ASCII and combine several large JavaScript files in a large project and you're talking a fair amount of saving. – Lloyd Mar 26 '13 at 21:17
  • @Lloyd Irrelevant. HTTP compression and smart minimizers (e.g. Clojure Compiler). –  Mar 26 '13 at 21:17
  • @Lloyd Okay, so "in some cases there may a few more bytes transmitted" so .. *aren't [really] significant*. –  Mar 26 '13 at 21:18
  • I voted as Not Constructive. However, I *prefer* to use the first approach while *keeping in mind* that only functions introduce new scopes. There will be *no* [real world] performance difference due to hoisting. –  Mar 26 '13 at 21:20
  • @pst to me it seems like a very valid question. It might be a duplicate (I'm too lazy to check)... – Vatev Mar 26 '13 at 21:22
  • It's equivalent to having the var on top of the scope, check this answer: http://stackoverflow.com/questions/2731123/javascript-var-declaration-within-loop – Ignacio Mar 26 '13 at 21:24
  • 2
    OP asks if there is any difference between placements of `var` (other than readability), that is a valid and good question IMO. The debate about saving 3 bytes of source code is not... – Vatev Mar 26 '13 at 21:25
  • 1
    @dqhendricks Variables are not objects and the author already knows about hoisting. He/she has the tools to answer themselves. –  Mar 26 '13 at 21:25
  • 3
    The largest downside to this is the infinite loop. – Travis J Mar 26 '13 at 21:36
  • I don't find the question very constructive and it will probably just lead to debates. – Nope Mar 26 '13 at 21:40

5 Answers5

5

In JavaScript - The Good Parts Douglas Crockford suggests that by using the second method and declaring your variables at the top of their scope you will more easily avoid scope bugs.

These are often caused by for loops, and can be extremely difficult to track down, as no errors will be raised. For example;

function() {
  for ( var i = 0; i < 10; i++ ) {
    // do something 10 times
    for ( var i = 0; i < 5; i++ ) {
      // do something 5 times
    }
  }
}

When the variables are hoisted we end up with only one i. And thus the second loop overwrites the value, giving us an endless loop.

You can also get some bizarre results when dealing with function hoisting. Take this example:

(function() {
  var condition = true;
  if(condition) {
    function f() { console.log('A'); };
  } else {
    function f() { console.log('B'); };
  }
  f(); // will print 'B'
})();

This is because function bodies are hoisted and the second function overwrites the first.

Because searching for bugs like this is hard and regardless of any performance issues (I rarely care about a couple of microseconds), I always declare my variables at the top of the scope.

Jivings
  • 22,834
  • 6
  • 60
  • 101
  • Yeah I've read "JavaScript - The Good Parts". I wasn't looking for an answer about best practices though. I wanted to know what was going on under the hood. – Philip Walton Mar 26 '13 at 21:38
  • 1
    I must have missed that last sentence :) Nevertheless, I think this is much more important. – Jivings Mar 26 '13 at 21:40
  • There is *no such thing as function hoisting*. The last example presented is *invalid JavaScript* and both IE and FireFox (and likely Chrome) treat it wrong or "buggy" - but in different ways. The only correct way to handle it is to throw a Syntax Error as all other alternatives violate the specification. Function Statements may only appear as a top-level Statement in a Function Body or a Program. –  Mar 26 '13 at 22:07
  • Also, the number of "bugs" that are "extremely difficult to track down" that I have run into when using `var` locally .. 0. None. Nada. Zilch. Not only that, the argument presented is dubious as `var i, j; for (i = 0; i < 10; i++) { for (i = 0; i < 5; i++) { } }` is a bug that is just as "extremely difficult to track down". Neither will work correctly. –  Mar 26 '13 at 22:11
  • Crome (27) printed the second example as "B", but FF (19) printed it as "A". – Philip Walton Mar 26 '13 at 22:14
  • @PhilipWalton Just as broken :) –  Mar 26 '13 at 22:15
  • @pst I'm sure I could think up some more compelling examples than these, but I thought they were good enough. I know in strict mode you can't have two functions with the same name, but I haven't heard it enforced that function statements have to be top-level. I wouldn't be surprised though, it makes perfect sense. Can you point me at the relevant section in the ECMAScript spec? – Jivings Mar 26 '13 at 22:37
  • @Jivings It has nothing to do with two functions of the same name. This is *valid* (but very ugly) code: `function x () { function x () {} function x () {} }`. Look in the Grammar Section - it relates to *where* the Function Statement grammar production is allowed. (So find the grammar rules that *allow* a Function Statement; the inverse is where it is illegal to appear.) –  Mar 26 '13 at 22:40
  • @pst All I can see is A.5 and clause 14. I assume that's what you're referring to. Nonetheless, the first example is valid and I brought it up because I had an identical bugfix today :) – Jivings Mar 26 '13 at 23:01
  • @Jivings A FunctionDeclaration production can be derived from a SourceElement, however, blocks are StatementLists (and no valid grammar can derive a FunctionDeclaration from a Statement without a FunctionExpression which can introduce new SourceElements). See also: "Several widely used implementations of ECMAScript are known to support the use of FunctionDeclaration as a Statement. However there are significant and irreconcilable variations among the implementations in the semantics applied to such.." Browsers accept *invalid grammar* with *implementation-sepcific behavior*. This is a bug. –  Mar 26 '13 at 23:13
3

There will not be any differences during execution time. There might be a imperceptibly small difference in interpretation/compilation time, but that of course will be implementation dependent. There also might be a few bytes different in the size of the file, which could also affect download time. I don't think either of these are worth being bothered about.

As you already know, any variable declaration will be hoisted to the top of the function. The important thing to note is that this occurs during the interpretation/compilation process, not during execution.

Before a function is executed, the function must be parsed. After each function is parsed, they will both have all of the variable declarations moved to the top, which means that they will be identical and there will be no execution time cost incurred.

For the same reason, there are no memory cost differences. After parsing, there will be no differences at all.


Since you are not asking about style I am not telling you which I think is better. But I will say that the only reason you should prefer one over the other is style.

Peter Olson
  • 139,199
  • 49
  • 202
  • 242
  • 2
    "During the parsing of a function" might be more accurate. There is no guarantee of compilation and `var` hoisting can - and must - be exactly rewritten *before* a function is executed. –  Mar 26 '13 at 21:23
  • @pst You're right, I was just using the term "compiling" rather loosely. – Peter Olson Mar 26 '13 at 21:25
  • @pst do you have a source for this statement? "`var` hoisting can - and must - be exactly rewritten `before` a function is executed"? – Philip Walton Mar 26 '13 at 21:34
  • @PhilipWalton Not rewritten, that bit was a carryover from an edit and is misleading. –  Mar 26 '13 at 21:36
3

Style

Subjective. I prefer the approach that keeps the var close to the usage site, but I always keep the scoping rules in mind. I also avoid combining multiple declarations into a single var and prefer multiple var statements.

Memory allocations

Variables are not objects: not applicable. Due to the hoisting rules, the variable "slot" has the same lifetime in all cases.

Performance

No. There should be no difference in terms of performance. While an implementation could technically really mess this up - they don't.

The only way to answer this (besides looking at every implementation in minutia) is to use a benchmark.

Result: noise differences on modern browsers

  • 2
    I also made a jsperf for this: http://jsperf.com/inner-versus-outer-var-declarations which shows a negligible difference. – Travis J Mar 26 '13 at 21:40
  • @TravisJ: Nice :) I think in a bigger picture getting into a habit of not declaring your variables at the top of the scope will have a higher risk of leading to hard-to-debug errors than the other way around. – Nope Mar 26 '13 at 21:42
  • 1
    @FrançoisWahl I disagree - but I leave such in the realm of "subjective" :) –  Mar 26 '13 at 21:43
  • @pst: I come myself from C# so I started applying my `var` declarations at block level, assuming at the time `if` statements and similar have scope. That caused my a lot of hours of debugging when unknowingly over-writing my variables. Yes though, I agree it is subjective +1 to that :) – Nope Mar 26 '13 at 21:45
  • I don't understand the difference you mentioned--an unitialized variable has an initial value of `undefined` regardless of whether it was hoisted. – Peter Olson Mar 26 '13 at 21:48
  • @PeterOlson Good call. I was thinking of another case when I wrote that. Does not apply here. Any takes on the IE10 anomaly? –  Mar 26 '13 at 21:49
  • @pst - One thing that I am having some problems finding clarity on is your claim that `Variables are not objects`. Are you sure about that? From what I could tell, `var j = 10` is a Number object as defined here: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf on page 154. Moreover, that since `typeof j` would reveal `Number` that j has indeed gone through the `Number` constructor. Can you provide some details on why this variable is not an object? – Travis J Mar 26 '13 at 21:53
  • @FrançoisWahl most Crockford-like JS best practices assume that the learner of JS is coming from another language. If the learner isn't coming from another language, such broad advice can often lead to more confusion. I, myself, learned JS first, so block vs. function scope was never confusing to me. – Philip Walton Mar 26 '13 at 21:53
  • @TravisJ Variables *name* or *evaluate to* objects. A variable itself can represent *any* particular value (e.g. `undefined`, `"Hello"`, `{hi:2}`). But variables are not themselves objects. They are only ways to give names to objects/values. It is the object/value which results from the *evaluation of* the variable that is passed to `typeof`. Consider that `typeof "Foo"` is equally valid even though no variables were used. (The Reference Specification Type deal with properties and special operations like `del`.) –  Mar 26 '13 at 21:54
  • @pst I think the difference on IE10 was just a single-data-point fluke. I just tested it a few times on IE10 and got varying results. – Peter Olson Mar 26 '13 at 21:57
  • @PeterOlson Thanks, that was really throwing a loop in my head/argument. –  Mar 26 '13 at 22:00
0

Your example will function exactly the same. It could be even more unclear than your example to use 'var' in a block, though. Say, for example you want to update a variable that is outside of the scope (by not using 'var') conditionally, or use a local 'var' instead. Even if that condition is false, 'j' becomes local. It turns out not to be as trivial as it appears to do that.

var j = 1;
function foo () {
    j = 2;
    if (false) {
        var j = 3; // makes `j = 2` local simply for being in the same function
    }
    console.log(j);
}
foo(); // outputs 2
console.log(j); // outputs 1

That's one tricky case that may not work as you expect just by looking at the code.

There are no downsides to declaring every 'var' on top, only up-sides.

Spencer Lockhart
  • 1,164
  • 7
  • 7
  • This is referred to as "hoisting", by the way. All variable declarations are hoisted to the top of the function, so it behaves as if you wrote everything at the top anyway. – Colin DeClue Mar 26 '13 at 21:26
  • 2
    @ColinDeClue, The OP knows this is hoisting, and it doesn't act like "you wrote everything at the top anyway", it acts like `var j;` was written at the top regardless of where 'var j' is, in the function. Hoisting does not set 'j' to 3 regardless of being in a falsy condition, though. Because of the confusion and ugliness of hoisting in general, all variables should be declared at the top of it's scope. – Spencer Lockhart Mar 26 '13 at 21:37
  • Sorry, yes, I didn't mean that it acts like everything was written at the top, just that it behaves as if all declarations are at the top. Also, yes, there certainly are downsides to declaring every 'var' at the top. If you happen to have a particularly long function (which you shouldn't) it decreases readabilty be moving declaration further from utilization. – Colin DeClue Mar 26 '13 at 21:40
  • @SpencerLockhart Hoisting is ugly? How so? Besides a subjective claim which doesn't warrant a "should be", I can otherwise agree - from the other view ;-) The I find it (multiple/local var statements) much less ugly than a single outer "one var to rule them all". –  Mar 26 '13 at 22:18
0

The 2nd way saves the 4 characters in the the javascript file - but in terms of the actual code generation or execution speed I don't believe there is any difference at all.

MrPurpleStreak
  • 1,477
  • 3
  • 17
  • 28