My question is, where do I declare the baz
variable?
So the generic answer to this question is "wherever you want", but of course that's not helpful and has pitfalls, so I'm going to tell you where I recommend putting it, and attempt to defend my logic as best as possible.
No matter where you declare a function* or variable** in JavaScript, the function or variable will be hoisted to the top of its containing scope***. If there is no containing scope, it will be implicitly added to the global namespace (in browsers this is window
, in NodeJS this is global
).
When a variable or function is hoisted, it means that the JavaScript interpreter will pretend as though your variable declaration were written as the first line of the containing scope.
In practice what this means is that when you write:
(function () {
...a...
if (...b...) {
var example = ...c...;
// ^^^^^^^^^^^
}
}());
It's actually evaluated as:
(function () {
var example;
//^^^^^^^^^^^
...a...
if (...b...) {
example = ...c...;
}
}());
* I'm referring to function declaration here. I.E. function name(...) {...}
** I'm referring to variable declaration with var
here. I.E. var name
*** Containing scope is typically the wrapping function, but sometimes for various reasons there is no wrapping function.
Isn't it safer to code as follows, where var
is used where the variable is first used?
Because of hoisting this classic best practice becomes susceptible to simple mistakes.
Consider the case where you write some code...
(function () {
for (var i = 0; i < x.length; i++) {
...do stuff...
if (...something happens...) {
break;
}
}
...more stuff...
...even more stuff...
doSomethingWith(i);
}());
Then you come back later, and need to add some new functionality, so you add:
...more stuff...
for (var i = 0; i < y.length; i++) {
...do stuff...
}
...even more stuff...
Now, if you combine the two it's easy to see in this contrived example that sloppiness has led to accidentally redeclaring and overriding the i
variable.
This might seem silly, but it happens all the time. It's quite easy if you don't actively take steps to avoid it. This sort of an issue isn't a problem in languages with block scope, because i
would be contained in each for
loop's scope, and to use doSomethingWith
, we would have had to expose the value of i
to the parent scope.
With that all said, your question is really one of best practices. This is inherently subjective and will lead to disagreement. I'm going to post my opinion and I will attempt to defend it, but it should be treated as an opinion and not dogma.
In my opinion, functions should have the following flow:
- Directives
- Variable declaration
- Function declaration
- Variable instantiation
- Other code
(1) Directives are things like "use strict"
. They come first by definition.
(2) Variable declaration using var
comes second. They could come after function declaration, or even in between, but I've found it makes them harder to find. I also recommend using exactly one var
statement with each var placed on separate lines, organized alphabetically. This forces variables to be clustered together, and alphabetical ordering prevents accidental duplication.
(3) Function declaration comes next because functions are hoisted just like variables. Seeing what local APIs are available within a given scope is something I find useful.
(4) Variable instantiation comes after function declaration, and in my opinion should be separate from declaration. Yes there are circumstances where it's easier to just put them on the same line, but as a general rule, you can't go wrong with keeping them separate.
The reasoning for this one is two-fold.
First, the declaration will be interpreted to happen before the assignment, so writing code that reads the way the interpreter is going to interpret it will help with understanding.
Second, if the assignment is merged with the declaration, order suddenly starts to matter, and you lose the ability to consistently alphabetize your variables.
As an example:
(function () {
var x = ...x...,
y = ...y...,
length = Math.sqrt(x * x + y * y);
// ^^^^^^ no longer alphabetical
}());
If you separate the declaration from assignment, you can get the benefits of both:
(function () {
var length,
x,
y;
x = ...x...;
y = ...y...;
length = Math.sqrt(x * x + y * y);
}());
(5) Once you're done with all the setup for a function, you'll need to write the rest of the code. This step is the "rest of the code" step.
If you've been paying attention to ECMAScript2015, then you should know that there are two new ways to declare variables: let
and const
.
These new variable declarations use block scope. If you're assigning a variable that will never be overwritten, use const
, otherwise use let
.
Going back to:
Isn't it safer to code as follows, where var
is used where the variable is first used?
let
allows you to do this. If you can use let
, stop using var
; use let
and const
where the variables are first used; combine declaration with assignment:
ES5 way:
(function () {
var example;
...
example = ...;
}());
ES2015 way:
(function () {
...
let example = ...;
}());
let
doesn't work with iOS 9, apparently (i.e. any not-the-newest iPads/iPhones).