views:

97

answers:

3

If a variable could be defined in a function, even if no value is assigned, it becomes a local variable

so, is testB() better programming?

var test = 'SNAP!'
function testA(boolean) {
    if (boolean) var test = 'OK';
    else var test = null;
    alert(test);
}
function testB(boolean) {
    if (boolean) var test = 'OK';
    alert(test);
}
testA(true); // 'OK'
testB(true); // 'OK'
testA(false); // null
testB(false); // undefined, no error

In my specific case test's global value ('SNAP!') is neither expected nor required.

+4  A: 

If the variable does not need to be manipulated by any other functions, keep the variable inside a function with var foo;.

Otherwise, if it does need to be accessed and read in multiple scopes, keep it outside. But remember that when you keep it outside, it becomes global. That is, unless you wrap everything in a self executing function, which is the best way:

(function() {
   var president='bush';

   function blah() {
     president='reagan';
   }

   function meh() {
     president= 'carter';
   }

   document.getElementById('reagan').onclick=blah;
   document.getElementById('carter').onclick=meh;


})();

alert( president ) // undefined

The above is perfect for a variable accessed by functions defined inside of that scope. Since there are 2 elements i click to set the president, it makes sense to define it outside both functions because they set the same variable.

So, If you are not dealing with multiple functions changing the exact same variable, keep them local to the function.

meder
I get your point, I discovered this when debugging in a situation where I wanted to use test's global value ('SNAP!'). But, in this case I do want it to be null and not any global value. I just wondered if it was considered a good practice. Thanks.
Carlos Gil
alert (president) would throw an error
Juan Mendes
@Juan Mendes - right. That's for demonstration.
meder
@meder To make it accurate without an error, just change the last line to alert(window.president)
Juan Mendes
+2  A: 

Is testB better programming? No, because it gives an unexpected result of "undefined" (at least, I was surprised by that) and it is hard to read.

Generally, variables should be limited to the scope that requires them, so if the "test" variable is not needed outside the function it should be declared local. To avoid confusion, declare your variable before using it:

function testC(boolean) {
    var test;
    if (boolean) {
        test = "OK";
    }
    else {
        test = null;
    }
    alert(test);
}

Unless you genuinely want to change the global scope version of "test", in which case don't use the var keyword inside the function.

If you ever find yourself using the same name for a local variable and a global variable you might consider renaming one of them.

Cameron Skinner
Well, now that I expect it, and want it (I don't need 'SNAP!'), why is it not OK? Performance? Or just readability?
Carlos Gil
Just readability and maintainability (which are almost synonyms). If you have a global variable when you don't need to, one day you will accidentally overwrite it somewhere else and spend a day figuring out why everything broke.
Cameron Skinner
Or more well defined, globals add tight coupling between your module and the environment. If you need to reuse your module, you'll have to chase those globals, and replicate them in a separate environment. If you do need to share a variable between a couple of functions, the self calling function is a great pattern
Juan Mendes
True, s*** happens. But that day already came for me, and in a really long function too. But, NOW that I know this, debugging or maintaining testB() isn't more complicated to me than testC(). Thanks.
Carlos Gil
+4  A: 

You can't declare variables conditionally.

Why?

The variable instantiation process occurs before the actual code execution, at the time the function is executed, those variables will be already bound to the local scope, for example:

function foo () {
  if (false) {
    var test = 'foo'; // never executed
  }
  return test;
}
foo(); // undefined

When the function is about to be executed, identifiers of formal parameters, identifiers from variable declarations, and identifiers from function declarations within the function's body are bound to the local variable environment.

Variables are initialized with undefined.

Also, identifiers in the local scope shadow the others with the same name, higher in the scope chain, for example:

var test = 'global';
function bar () {
  alert(test); // undefined, not "global", the local variable already declared
  var test = 'xxx';
}
bar();

If the test variable were not declared anywhere, a ReferenceError will be thrown:

function foo () {
  return test;
}

try {
  foo(); // ReferenceError!!
} catch (e) {
  alert(e);
}

That's one of the reasons about why for example, JSLint recommends only one var statement at the top of functions, because for example, the first snippet, will actually resemble this when executed:

function foo () {
  var test; // var statement was "hoisted"
  if (false) {
    test = 'foo'; // never executed
  }
  return test;
}
foo(); // undefined

Another reason is because blocks don't introduce a new lexical scope, only functions do it, so having a var statement within a look might make you think that the life of the variable is constrained to that block only, but that's not the case.

Nested function declarations will have a similar behavior of hoisting, they will be declared before the code execution, but they are initialized in that moment also:

function foo () {
  return typeof bar;
  // unreachable code:
  function bar() {
    //..
  }
}
foo(); // "function"
CMS
Just to expound on this...I believe what CMS (and JSLint) mean by "one var statement per function" is, regardless of how many variables are declared, declare them all in a single line at the top with commas to separate them like this: `var test = 'test', value = 'value', run = 'around';`
treeface
@treeface, yep, the second reason why JSLint recommends this, is that `Block` statements (such the `{}` blocks used after Iteration statements, `if` statement, etc.) don't introduce a new scope, only functions introduce a new lexical environment where variables can be declared. So, having one `var` statement at the top of the function, can avoid confusion.
CMS
Thanks. I added something to the question to clarify that I did not expect to declare variables conditionally, I had already learnt that. My question was more about what you said about JSLint (will be checking that out, in the mean time), is performance a factor for that recommendation?
Carlos Gil
Good question, Carlos. I just searched around for an answer to this and found [this awesome site](http://jsperf.com/multiple-variable-statements). It looks like the difference is extremely minor, but I haven't run a test yet where a single var doesn't come out on top.
treeface
@treeface: I just tried it, and the one with multiple var statements was the fastest by a few %, the first time, but on second attempt it was the single that was faster by 0%. It is extremely hard to measure time in browsers because they work in a multitasking environment and you never know how much time was used by the browser. Since it isn't measurable, use the one that makes the code more readable. "Premature optimization....."
some