Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your code easier to read. We've been using them in all the hapi modules for over 4 years now.
JavaScript makes is harder than most languages to know where variables are coming from. Variables assigned required modules are particularly important because they represent a singleton object shared with the entire application. There are also globals and module globals, along with function variables and arguments.
Traditionally, variables starting with an uppercase letter represent a class that must be instantiated using new
. This was an important semantic in the early days of JavaScript but at this point, if you don't know Date
requires new Date()
you are probably very new. We have adopted CamelCase variable names for all module global variables which are assigned required modules:
var Hapi = require('hapi');
Note that you cannot new Hapi()
, only new Hapi.Server()
. In this style, the exported object from the modules always exposes an object which contains the API. This means a single function module should still export an object with the single method as an object property:
exports.add = function (a, b) {
return a + b;
};
This makes it trivial to identify which variables in your code represent required modules. The language itself does contain a few uppercase variables but those are well known and should not cause any confusion.
When a module (any node file) contains its own prototype class, that class variable also starts with an uppercase which can cause confusion. However, we do not allow any module global variable except for one: internals
.
var Hapi = require('hapi');
var internals = {};
internals.Server = function () {
this.server = new Hapi.Server();
this.server.connection();
};
internals.server = new new internals.Server();
// This is not allowed:
var server = new internals.Server();
This means that within any function in your code, uppercase variables are either required modules APIs or JavaScript natives. Any internals
prefixed variables are module globals which act as a singleton. The rest are either function variables or arguments which are easier to spot because of the smaller scope.
A note about singletons - I often see bugs caused by developers using a module global variable used by a module prototype. For example:
var internals = {};
internals.cache = {};
exports.Cache = internals.Cache = function () {
};
internals.Cache.prototype.get = function (key) {
return internals.cache[key];
};
internals.Cache.prototype.set = function (key, value) {
return internals.cache[key] = value;
};
The problem is that multiple instances of the cache will all share the same memory:
var Cache = require('./cache');
var cache1 = new Cache();
var cache2 = new Cache(); // Uses the same memory cache1 uses
By requiring the use of internals
as a prefix, this bug becomes obvious to spot. When I code review a pull request, I always search for all the instances of internals
to make sure it only holds static configuration and other safe data to share between instances. This is much harder to spot with out the prefix.