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 constant
- 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 4 or more items and consider it at 3 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.
Object.hasOwn to check the existence of a property-value pair.
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);
date.toLocaleString();
date.toLocaleTimeString();
date.toLocaleDateString();
// and other toString variantsYes
new Set();- No duplicates are wanted
- The order is irrelevant
Yes
new Map();In the following situations:
- Keys are something else than string or symbol
- Continuous adding and removal of key-value pairs
- Keys are unknown or can be anything
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.
Yes.
The chain stops when expecting a value from undefined. Know the difference between those:
(undefined)?.startsWith(`a`); // undefined
{}?.startsWith(`b${1+2}`); // TypeError and what is inside the parentheses is computed
{}?.startsWith?.(`c`); // undefined
{}.a?.b?.c?.["d"] // undefinedIn the second example we expect a value from undefined and the chain immediately stops.
In the second example we expect a value from an object (startsWith), which resolves to undefined, and then we call it (cannot call undefined).
In the third example we also use an optional call (?. + parentheses) to only call it if it is callable.
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.
Prefer promises over callbacks for one-time futures. Use async, await , Promise.all.
Always 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, as they allow for extension and are always live bindings. 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 FastAverageColor from "fast-average-color/dist/index.esm.js";
import { x } from "./x.js";
import * as z from "./z.js";
const y = 5;- exports with optional line break
- imports of standard library
- imports of runtime library (NodeJS, Deno, etc)
- imports of external dependencies
- imports of local files
- optional line break
- declaration of constants that are import related (some libraries only export default)
- optional two line breaks then begin of code
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 directly.
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);Avoid when possible. Regular expression is a powerful language that overlaps with JS constructs. Often a single line can obscure recursive and complex instructions. The tooling to debug and assess performance of regexs are lower than JS.
Avoid the constructor syntax. Use named groups.
let r = /[a-z]+/;Regex are sometimes used to extract information out of a big string. Consider using an object instead that can be extracted with JSON for example.
String.prototype.includesString.prototype.indexOfString.prototype.replaceString.prototype.endsWithString.prototype.startsWith
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 for code clarity it makes it obvious which one is expected to fail. Generally avoid try catch entirely if possible. Validate input as early as possible to avoid potential error management in the middle of the function body.
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 MACRO_CASE (all caps with underscores) for top level constants that do not change across versions and runs.
Package/module names and script names with Kebab case (lowercase with dashes). No dots.
Default imports use the same letters as the package name.
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.
- Name, version, description, license, author, homepage (What is it ?)
- type, main, module, browser, exports, bin (What is the entry file ?)
- scripts (What commands are available ?)
- dependencies, optionalDependencies, peerDependencies devDependencies (What does it use ?)
- configs, engines, os, cpu (How is it configured ?)
- files, repository, keywords, private, publishConfig (Meta, publishing, distribution)
Avoid peerDependencies because they create more problems than they solve, instead specify them in the documentation.
{
"name": "utilsac-example",
"version": "15.0.0",
"description": "Utility functions",
"license": "CC0-1.0",
"type": "module",
"main": "utility.js",
"repository": {
"type": "git",
"url": "git://github.com/GrosSacASac/utilsac-example.git"
}
}Install eslint and eslint-red rules
npm i -D eslint @eslint/js eslint-config-red globals @stylistic/eslint-plugin
Inside your root folder copy eslint.config.js
If code is meant for other environments other than browser, add them after ...globals.browser
Inside package.json > scripts
"lint-fix": "eslint --fix",
"lint": "eslint",
- GrosSacASacs
- fschoenfeldt
Public Domain
