I don't like the single-var rule.
It annoys me for several reasons.
Here's a slice of code. What does it signify?
peter: payPeter().toRob('paul'),
simon: says(new Sayer()),
alice: coop(3).r
},
alpha = new Beta(config),
callback = new Maybe(fn).lift(iterable),
self = this
resolver = onError => {
try {
callback(self);
} catch (e) {
onError(e);
}
},
bonus = {
Well, you know it's a set of variables, because you guess as much from the context. It's not exactly obvious, though. At a glance, am I looking at variables or the fields of an object? In very large files it's tough to say. And are these declared with var
, let
or const
? This difference matters in ES6.
This style also buries an error - the code will leak a global variable that it wouldn't if everything was prefixed with var
.
Most debuggers will treat code like var x = a, y = b, ... = z;
as a single line, and won't allow the developer to step over each individual assignment. This behaviour has changed in Chrome recently, but many other devtools environments will still treat the var operation as a single step.
One nice thing about free vars is that we can create them at the exact moment they are needed:
for (let i = 0; i < y; i++) {...}
This makes them a little easier to name. It also makes it easy to delete variables as soon as they're no longer needed.
Especially when using const
. Do we indent successive vars using a single tab (easy to type, but looks ugly), or do we manually align each var with spaces?
const a = 12,
b = 13,
c = 14;
Every time I add a variable to a function, I have to chop and change the position of a semicolon. PRs look more noisy than they need to be:
>>>
y = 25;
===
y = 25,
z = 26;
<<<
The single-var rule was popularised by Douglas Crockford, who believed it was necessary to protect developers from the curious scoping behaviour of var
. In most C-like languages, variables are block scoped - but in JS, they're scoped to the function.
For instance, in the following loop, the iterator i
exists and can be read outside the loop. This is a surprise to a developer coming from most other languages:
for (var i = 0; i < 10; i++) {}
console.log(i); // prints 10!
This wouldn't work in C++, for instance:
#include <iostream>
int main()
{
for(int i=0; i<10; ++i) {}
std::cout << i;
}
// COMPILE ERROR
// In function 'int main()':
// 7:16: error: 'i' was not declared in this scope
Because a C++ developer might assume JavaScript developers are block scoped too, putting all the vars at the top of the function seemed like a pragmatic style that would ensure such a programmer would never 'misread' the scope and longevity of a var.
And that's great. But we don't live in the 90s any more.
I wish we did - the music was better and you could buy houses - but the world has changed. Grunge is over. Side partings are no longer cool. Most importantly, we have ES6, and that gives us let
and const
, both of which have 'block scoping' (like C), as opposed to the 'function scoping' of var
:
for (let i = 0; i < 10; ++i) {};
console.log(i); // ReferenceError: i is not defined
So. The single var rule is not needed, not wanted (by me) and not helpful. I propose we get rid of it!