1

I'm reading the book JavaScript: The Good Parts.

On page 113 it recommends function expressions instead of function statements, because statements are subject to hoisting:

The statement:

function foo( ) {}

means about the same thing as:

var foo = function foo( ) {};

Throughout this book, I have been using the second form because it makes it clear that foo is a variable containing a function value.

Further JSHint warns against function statements defined in a block, e.g. (my example):

if (foo) {
    function baz() {}
}

... e.g. because support for this isn't consistent between javascript engines; it produces the following error message:

Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.

A recommended work-around is a function expression, e.g. (my example):

if (foo) {
    baz = function() {}
}

My question is, where do I declare the baz variable?

The end of page 36 (the Scope section of the Functions chapter) says,

In many modern languages, it is recommended that variables be declared as late as possible, at the first point of use. That turns out to be bad advice for JavaScript because it lacks block scope. So instead, it is best to declare all of the variables used in a function at the top of the function body.

Does that mean that it's best to declare the baz variable at the top of the function?

For example, like this:

function(foo) {
    // declare variables
    var baz;
    // implementation
    if (foo) {
        baz = function() {}
    }
}

Is this a best practice, or am I misunderstanding something, reading too much into it? I fear it makes it harder to maintain: because it separates the declaration of baz (i.e. var baz;) from its initialization. If I rename or delete one but not the other then suddenly the variable would exist at global scope.

Isn't it safer to code as follows, where var is used where the variable is first used?

function(foo) {
    // implementation
    if (foo) {
        var baz = function() {}
    }
}

I understand that the latter is functionality equivalent to the former. Is the latter contrary to a best-practice coding-style for Javascript?

  • 3
    Use ES6, which doesn't have these issues. – gardenhead Oct 25 '16 at 13:44
  • ES6 let doesn't work with iOS 9, apparently (i.e. any not-the-newest iPads/iPhones). – ChrisW Oct 25 '16 at 14:01
3

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:

  1. Directives
  2. Variable declaration
  3. Function declaration
  4. Variable instantiation
  5. 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 = ...;
}());
5

Javascript variables only have 2 scopes, global and local. A variable declared inside a function (inside an IF or not) is accessible to the whole function.

Personally I declare variables toward the top of a function for the sake of clarity, and with the above in mind. Therefore I am in agreement with the advice given in your book.

You may want to look into "let" and "const", part of ECMAScript 2015 (ES6).

1

I fear it makes it harder to maintain: because it separates the declaration of baz (i.e. var baz;) from its initialization. If I rename or delete one but not the other then suddenly the variable would exist at global scope.

I'm not sure I see the issue here, really. Any time you change a variable name in one place, you have to change all other users of it, as well. Otherwise it causes a problem.

This is true whether or not you declare the variable at the point the function is defined. It's true that putting the declaration at the top adds one extra occurence of that variable, but it is a trivial one (and not one you are likely to miss if you have a practice of always declaring variables at the top).

On the other hand, declaring variables at the top of the function helps you avoid accidental name collisions, and makes it easy to see in one place what variables exist within the scope.

0

The Google JavaScript Style Guide recommends the following:

Instead use a variable initialized with a Function Expression to define a function within a block:

if (x) {
  var foo = function() {};
}

They don't have a recommendation that you should declare variables at the top of the function (before they're used).

Neither JSLint nor JSHint warn that this is wrong.

0
function(foo) {
// implementation
if (foo) {
    var baz = function() {}
 }
}

This makes it seem like baz is scoped to the if block, but it's scoped to the whole function.

Keeping it at top to signal that its in the whole function's scope is a good idea.

This style is seen in other programming languages as well. For example in ML we initially declare all variables in a block using let.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.