red is life passion fire
- Readability
- Crystal clear way to add and subtract functionality
- Minimal vocabulary
- Modern
- Features to use
- Features to avoid
- Features to never use
- Details
Use trailing commas.
Prefer const over let, no var.
Explicit type conversion with the functions String, Boolean, Number, Array.from, Object.fromEntries.
return, throw as early as possible to avoid huge indentation levels.
Make every line of code independent if possible, do not use chain variable assignments.
Use an expression because:
- it works as assignment
- as parameter for another function
- as IIFE
- compatible with arrow and original function syntax
- ability to mark as const
- easy to alias
- easy to decorate
- no hoisting
Ideally less than 4 parameters. If more consider using an objects and destructuring. Always use explicit return for arrow functions.
const x = function (a, b) {
return a - b;
};
const y = (a, b) => {
return a * b;
};Declare all fields as soon as possible even if they don't have a value yet, put the creator/constructor function first. A seal on the object returned by the constructor (return Object.seal(hero)) should not throw any error.
const createHero = ({ name }) => {
const hero = {
name,
hitPoints: 100,
location: [0, 0],
favouriteAttack: undefined,
};
return hero;
};
const moveHero = (hero, [x, y]) => {
const [pastX, pastY] = hero.location;
hero.location = [pastX + x, pastY + y];
};
const teleportHero = (hero, location) => {
hero.location = location;
};Alternatively omit "Hero" in function names to avoid renaming.
export {
createHero as create,
moveHero as move,
teleportHero as teleport,
};import * as Hero from "./hero.js";
const hero = Hero.create({ name: `Superjhemp` });
Hero.move(hero, [5, 20]);
Hero.teleport(hero, [100, 100]);Use multiple lines for 3 or more items.
.length = 0 to reset it.
[] to create a new array.
.push() to append.
const array = [
4,
5,
6,
];Use map, forEach and other array methods to loop over an array.
Array.isArray for type checking.
object[key] = value; to add a key value pair.
delete object[key] to remove a key value pair.
{} to create a new object.
Use the shorthand when possible , computed properties
const b = 7;
const c = `key`;
const object = [
a: 5,
[c]: 9,
b,
];Use Object.keys, Object.values, Object.entries to convert it to an array, then use array methods to loop over an object. typeof x === `object` && x !== null for type checking.
Use backtick ` as they always work. ' and " can be included directly. Concatenation is done via ${x}. Adding line breaks just works. \n can also be used. There is no need to switch the delimiter.
const s = `string`;
const s2 = `Tom says: "That's my ${s}"`;typeof x === `string` for type checking .
Prefer Number.isFinite over isNaN. Number.isFinite can also be used for type checking. It returns false if the number is NaN, Infinity, -Infinity or another type. typeof x === `number` will return true if x is NaN, Infinity, -Infinity as well.
const mySymbol = Symbol();
const yourSymbol = Symbol(`A meaningful description`);
yourSymbol.description;Do not use Symbol.for because they are disguised globals. typeof mySymbol === `symbol` for type checking.
Use numbers to store dates. Use the Date built-in to display them.
Store the current time.
const time = Date.now();To display the date.
const date = new Date();
date.setTime(time);
d.toLocaleString();
d.toLocaleTimeString();
d.toLocaleDateString();
// and other toString variantsAlways use multi-line brackets.
if (condition) {
}
if (condition) {
} else {
}
if (condition) {
} else if (otherCondition) {
} else {
}
Put them at the top of the file as they will be executed first. Put exports before imports as they are executed before. Use named exports. Do not inline named exports in the middle of the file to make it obvious what is exported by reading the first line.
export { y };
import { x } from "./x.js";
import * as z from "./z.js";
const y = 5;Prefer promises over callbacks for one-time futures. Use async, await , Promise.all.
Historically it has been done with an immediately invoked function expression, now that let and const are available and block scoped use a simple block. With an iife:
// do not expose i
let nextSquare;
(function () {
let i = 0;
nextSquare = function () {
i += 1;
return i ** 2;
};
}());With a block:
let nextSquare;
{
let i = 0;
nextSquare = function () {
i += 1;
return i ** 2;
};
}Use undefined, avoid null. undefined is the default that is already used by the language, for example: destructuring when missing, default return value, unassigned variable etc.
true or false, use Boolean function to force cast to a boolean. Do not use !! to cast to a boolean. Leverage truthy values in if and while.
Use direcly.
setTimeout(() => {
}, 1000);avoid
window.setTimeout(() => {
}, 1000);Read Why disallow the class keyword
Avoid this. And associated bind, call, apply, class, prototype, super, new, extends, Object.create, Object.setPrototypeOf, Object.getPrototypeOf, __proto__, instanceof, typeof, .prototype.isPrototypeOf.
Any function can return an object, any function can take an object as first argument and operate on it. Every function can compose or combine results of other functions.
Prefer regular objects and functions over class. These can be exported from the same file and one can still use Object-oriented patterns without the class keyword.
bind can still be useful for currying with undefined as first value.
Prefer array spread syntax.
const numbers = [4, 5];
const max = Math.max.apply(undefined, numbers);const numbers = [4, 5];
const max = Math.max(...numbers);Prefer if else, as they can be extended and are cleaner.
Prefer if else.
Avoid
const x = y || z;
const r = z && u || w;Prefer if else.
Keep lines independent.
The comma operator is well known to write one-liners and hurts readability. Put each instruction on individual lines.
Prefer array built-ins to iterate.
Prefer exporting variables individually via named exports. Three shake friendlier and less runtime overhead.
Avoid
export { constants };
const constants = {
X: 1,
Y: 2,
};Prefer
export { X, Y };
const X = 1;
const Y = 2;No clear way to add or subtract functionality. Prefer named exports.
Avoid whenever possible. Avoid the use of Proxy, Object.defineProperty, Function.name and Function.length.
For setters that have additional functionality prefer explicit functions. Otherwise use public members.
Prefer explicit type conversion
const x = `1`;
// const y = +x;
const y = Number(x);const y = 1;
//const x = "" + y;
const x = String(x);Do not use the special arguments, use rest arguments instead.
Avoid == and !=, prefer === and !==.
Avoid relying on asi to be consistent and have less edge cases to remember.
Use try catch on individual statements that are expected to fail. Generally avoid try catch entirely if possible.
Avoid finally, in most cases, putting the statements after try catch has the same effect.
Avoid generators and associated yield keyword. Prefer functions that return a function with a closure.
Avoid any side effect inside the condition of an if. Same for while and for.
Be consistent.
Avoid,
const { body: { className } } = document;prefer 1 line per level.
const { body } = document;
const { className } = body;Avoid those. Prefer += and -=.
There is no need to remember the difference between --a and a--.
The increment amount can be something else than 1. It can be any variable.
It is consistent with other operators such as /= , *= , **= , %= etc
let a = 1;
a += 1;
a /= 2;Prefer explicit exported variables. With the exception of polyfills.
Prefer exporting new variables with different names.
Use object destructuring instead.
Strict mode throws error when doing things like assigning to undefined and will prevent mistakes. Strict mode is always enabled when using import/export.
These have no real purpose other than introduce subtle bugs. Use Boolean, String and array literal [] instead. new Object() is harmless but use the object literal shorthand {} for consistency.
Don't use the void operator, use a dedicated minifier instead.
Use semantic long descriptive names for variables. Do not use names that are already used by built-in globals or special keywords.
Camel case for regular variables and file names. No spaces.
Optionally Pascal case for creator/constructors.
Optionally all caps with underscores for top level constants that do not change across versions and runs.
Package/module names and script names with lowercase with dashes. No dots.
majo-ubjson as package name
majoUbjson.js entry file (not index.js)
bundle, minify-html as script names
// ALWAYS the same
const { PI } = Math;
const HALF_PI = PI / 2;
// regular variables that can be changed
const radius = 1;
const volumeSphere = (4 / 3) * PI * radius ** 3;
// constructor
const BookStore = class {
};
// creator Pacal (CreateHero) or camel case (createHero)
const createHero = function () {
const hero = {};
return hero;
};
// No !
// confusing
const await = 2;
// overshadows built-in
const Date = { year: 2019 };Indent with 4 spaces, or 1 tab.
Start with prettier.
If any non standard features are used, use should be documented. If any features are not in the standard track pipeline, the file extension should reflect that.
Prefer to avoid use of features that have not reached stage 4.
Yes when it helps document the code, for example by acknowledging a given situation.
Use constants at the top of the scope.
Install eslint 6+ and eslint-red rules
npm i -D eslint eslint-config-red
Inside package.json
"eslintConfig": {
"extends": ["red"],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {}
},
"env": {
"es6": true,
"browser": true
},
"rules": {}
}
- GrosSacASacs
- fschoenfeldt
Public Domain
