Main rules of a node.js code-writing
const
: use everywhere except loops and reassigning variableslet
: only in loops or redefined variablesvar
: don't use
Each variable must have a single sense, but a sense can be changed for a reassigned variable and code understanding will be decreased. const
prevents reassign possibility of variable.
Note βΉοΈοΈ: const
prevents only direct reassigning. Properties and methods of variable can be changed in any way!!! Read more here.
Wrong π₯:
class MyClass {
constructor() {
const EmailSender = require('email-sender');
exports.EmailSender = EmailSender;
this.email_sender = new EmailSender;
}
}
exports.default = MyClass;
Right π:
const EmailSender = require('email-sender');
class MyClass {
constructor() {
this.email_sender = new EmailSender;
}
}
exports.EmailSender = EmailSender;
exports.default = MyClass;
require
:
- Prevents situation when module wasn't installed but used in some function or class method. Script will be crashed only after invoking in other way.
require
"eat" process time - every invoke ofrequire
resolving of path to module and can decrease capacity of web-server by 20-25%(!) (related article (russian))
module.exports
/exports
:
- Prevents incontrolled exporting of data.
module.exports
can be changed asynchronously from any point of script. Alsorequire
-d data will be changed also (check node.js docs). Check example:
// o.js
const o = { a: 1 };
process.nextTick(() => o.a = 2);
module.exports = o;
// broke_mind.js
const o = require('./o');
console.log(o) // -> {a:1}
process.nextTick(() => console.log(o)) // -> {a:2}
const FS = require('fs');
const ChildProcess = require('child_process');
const GulpSASS = require('gulp-sass');
const MyPrettyClass = require('./MyPrettyClass');
const defaults = require('lodash/defaults');
const deepClone = require('lodash/deepClone');
const myMethod = require('./helper/myMethod');
Wrong π₯:
module.exports = class MyPrettyClass {
};
module.exports = function myfunction() {
};
Right π:
class MyPrettyClass {
}
module.exports = MyPrettyClass;
function myFunction() {
}
module.exports = myFunction;
By direct exporting will be created "anonymous" class/function. So you can export class/function without name:
module.exports = class {};
module.exports = function {};
Both defitions are correct.
It's not a real problem but some IDEs (for example, WebStorm) incorrecly highlight class's instance methods and properties and you cannot go to the class definition. Same problem with function also.
TODO
Wrong π₯:
const myFunction = () => { /* do smt */ };
Right π:
function myFunction() {
/* do smt */
}
Wrong π₯:
const addOne = item => item + 1;
[1, 2, 3].map(addOne);
Right π:
[1, 2, 3].map(item => item + 1);
Arrow function doesn't have own context and arguments
value so they direct place of usage is only internal of some other function defined by function
or method of class. In other places using of arrow functions don't have much sense.
Based on the above definition here is the answer to question "where must to be used function
and where "arrow function":
Named functions must be used only for reusing in two and more places or for code simplification.
Simplification means that this is arrow function, used only in one place but includes many code lines. Count of code lines are subjective (code complete said that count cannot be more then 200 lines).
list.map(singular => doSmt(singular));
list.reduce((several, parameters) => doSmt(several, parameters));
[1, 2, 3].reduce((sum, item) => sum += item, 0);
[1, 2, 3].map(id => ({ id })); // creates array of objects with "id" key
[1, 2, 3].map(item => {
const result1 = doSmt1(item);
const result2 = doSmt2(item, result1);
const result3 = doSmt3(item, result2);
return concatResults(result1, result2, result3);
};
const my_promise = new Promise((resolve, reject) => /* do smt */);
// ...
my_promise.then(results => {
/* do smt */
});
function postponePromiseInitialize(options) {
return new Promise(/* do smt */);
}
postponePromiseInitialize({
options: '',
in: '',
several: '',
lines: ''
}).then(result => /* do smt */
TODO
const my_first_promise = new Promise((resolve, reject) => /* do smt */);
const my_second_promise = new Promise((resolve, reject) => /* do smt */);
const my_third_promise = new Promise((resolve, reject) => /* do smt */);
// ...
my_first_promise
.then(results => /* do smt */)
.catch(err => /* catch error */);
my_second_promise
.then(results => /* do smt */)
.then(results => /* do smt */);
my_third_promise
.then(results => /* do smt */)
.then(results => /* do smt */)
.catch(err => /* catch error */);
- Classes names in CamelCase
- Instances names in snake_case
Example:
class MyPrettyClass {}
const my_pretty_object = new MyPrettyClass;
static
's always defined firstly in class definition
- For
static
methods use CamelCase:
class Example {
static staticMethodNameOnlyInCamelCaseName() { }
}
- For
static get
constats use UPPER_CASE:
class Example {
static get MY_PUBLIC_CONSTANT() { return 'same variable' }
}
static set
: don't use- Private/protected: need adds
_
character to name of method/property names:
class Example {
static get _MY_PRIVATE_CONST() { return PRIVATE_CONST }
static _myPrivateMethod() { /* do smt */ }
}
- All
static get
property must be beforestatic
methods
Wrong π₯:
class MyPrettyClass {
static myPrettyMethod() {}
static get MY_PRETTY_PROP() {}
};
Right π:
class MyPrettyClass {
static get MY_PRETTY_PROP() {}
static myPrettyMethod() {}
};
Plain's always defined secondary in class definition
- For
plain
methods use snake_case:
class Example {
my_plain_method() {}
}
- For
plain get/set
properties use snake_case:
class Example {
get value() {}
set value(new_value) {}
}
- All
plain get/set
properties must be beforeplain
methods
Example:
class ExcelReader {
static get EXCEL_FILES_FOLDER() {
return 'path/to/excels';
}
get reader() {
return this._reader;
}
set reader(reader) {
this._reader = reader;
}
constructor(reader) {
this.reader = reader;
}
read_excel_by_filename(filename) {
return this.reader.read(`${this.constructor.EXCEL_FILES_FOLDER}/${filename}`);
}
}
const excel_reader = new ExcelReader(new ExternalExcelFileReader());
excel_reader.read_excel_by_filename('excel_filename.xlsx').then(/* do smt */)