12

In my project (browser context only) I want to use a JS code quality tool. I've tried both jslint and eslint. I want linter to help me make my code clean, clear, errorproof and improve its overall quality. What I don't want to do is I don't want to write some dirty hacks or use bad practices just to make linters happy.

I'm concerned about only one issue. Both of them reported a problem that I'm using a function before it was defined. Obviously in the following code snippet bar won't be called before it's definition.

function foo() {
    bar();
}

function bar() {

}

foo();

In this simplest scenario I can just move bar before foo. But there are cases when it's just impossible. First function uses the second, the second uses the third and the third uses the first.

It seems like I can make linters happy by declaring all functions before their definitions like this.

var foo;

var bar;

foo = function() {
    bar();
};

bar = function() {

};

foo();

The questions are:

  • Is the first code snippet broken? I guess - not.
  • Is the first code snippet error-prone? I guess - maybe.
  • Is it a good practice to organize code like the second snippet (declare functions before defining them)?
  • If yes I should stick to this practice, shouldn't I?
  • If no what is the good practice regarding this issue?
  • Is this linter error worth paying attention to or should I just disable it?
Kolyunya
  • 5,973
  • 7
  • 46
  • 81
  • Have you tried switching positions of `foo` and `bar` in your code? – Justinas Aug 10 '16 at 11:14
  • @Justinas in this simplest scenario I can just move bar before foo. But there are cases when it's just impossible. First function calls the second, the second calls the third and the third calls the first. – Kolyunya Aug 10 '16 at 11:14
  • Maybe as far as I know the first case is very safe compared to the second. Even the *lint* blame. The reason is that in javascript the functions are executed only when are all defined, so you have a problem only if *bar()* will never be defined. In second case, you could execute *bar()* when it is undefined. Just because you call the *foo()* before the *bar()* definition. That because in the second case you're assigning functions to variables, that have a different behavior ind declaration/definition. – Mario Santini Aug 10 '16 at 11:23
  • I found [here](http://eslint.org/docs/rules/func-style) the explanation of the rule, that details better what I was trying to explain in my previous post. – Mario Santini Aug 10 '16 at 11:28
  • @MarioAlexandroSantini that's not the rule which triggers the error. The one which does is http://eslint.org/docs/rules/no-use-before-define – Kolyunya Aug 10 '16 at 11:44
  • 1
    You could look into [Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting) – Ron van der Heijden Aug 10 '16 at 11:55
  • `First function uses the second, the second uses the third and the third uses the first.` That's a pretty serious code smell. You probably either need to start doing some OO-style JavaScript -- or to refactor your code. I've been *exactly* where you are, fwiw, and bristled the same way *initially*. Then I caught the smell and found what was causing it. – ruffin Aug 15 '16 at 13:11

2 Answers2

2

No, the snippets are not broken but not best practice either.

var foo = function(){

}

var bar = function(){
  foo();
}

bar();

will actually become:

var foo, bar;

foo = function(){

}

bar = function(){
  foo();
}

bar();

Hence you should define all variables and functions at the beginning of the scope. JavaScript uses Hoisting which effectively moves all declarations for variables and functions to the top of the scope.

Doing it yourself is considered best practice and increases readability.

Eslint will check against the rule vars-on-top which is defined and documented here

https://www.reddit.com/r/learnjavascript/comments/3cq24a/crockford_says_hoisted_variable_declarations_are/ https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/

Tim Hallyburton
  • 2,741
  • 1
  • 21
  • 29
  • 1
    So the second code snippet is considered a good practice, right? – Kolyunya Aug 10 '16 at 11:27
  • About Hoisting, i am not expert but in js variable declarations are not moved to the top of the scope, only function declarations. example: function asd(){ console.log('1.: ' + asdvar); var asdvar = 'sir asdalot'; console.log('2.: ' + asdvar); } – kailniris Aug 10 '16 at 11:35
  • @kailnris the declaration is moved, not the asignment ;) – Tim Hallyburton Aug 10 '16 at 11:49
  • 1
    @TimHallyburton yes its true, in this example the variable is created in the local scope: function asd(){ asdvar = 'asd2'; var asdvar; } – kailniris Aug 10 '16 at 11:59
  • `So the second code snippet is considered a good practice, right?` I realize I'm repeating my earlier comment a little, but no, not *really*. In this simple case, there's no reason not to use `function foo` & `function bar`, I don't think. In the actual case (`a` calls `b`, which calls `c`, which calls `a`)... JSLint is a linter, and doesn't really intend to be a stand-in for **code review**, but it does help identify code *smell*. If you have incestuous calls like that & find yourself performing odd dances to get around rules, consider an overall code review as well. – ruffin Aug 15 '16 at 13:17
1
  1. Is the first code snippet broken? no it's not broken.
  2. Is the first code snippet error-prone? No.
  3. Is it a good practice to organize code like the second snippet (declare functions before defining them)? NO there are many other good ways
  4. If yes I should stick to this practice, shouldn't I? Yes
  5. If not what is the good practice regarding this issue? there are many good pattern you can follow
  6. Is this linter error worth paying attention to or should I just disable it? It's good to pay attention for keep your code clean

Use strict mode always. "use strict"

have you functions inside scope like IIFE for not having global's variables

read more about IIFE

function foo() {
    bar();
};

function bar() {

};
Jorawar Singh
  • 7,463
  • 4
  • 26
  • 39
  • The scope is not the problem. As you can see in his edit that putting the declarations on top of the snippet has solved the problem. It is likely that eshint checked against "vars-on-top" (as linked in my answer) – Tim Hallyburton Aug 10 '16 at 11:40
  • i understand but that's matter of choice from lint i guess :-) i would prefer to not having vars pre-defined on the top – Jorawar Singh Aug 10 '16 at 11:43