├── .eslintrc (do not use)
├── LICENSE
├── README-German-DE.md
├── README.md
├── eslint.config.js
└── nulldev-template
├── .editorconfig
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── Bug_Report.md
│ └── Feature_Request.md
├── .gitignore
├── README.md
├── eslint.config.js
├── package.json
└── src
├── app.js
└── utils
├── configHandler.js
└── logger.js
/.eslintrc (do not use):
--------------------------------------------------------------------------------
1 | // OLD FILE FOR REFERENCE ONLY!!!
2 | // USE eslint.config.js INSTEAD!!!
3 |
4 | { // NullDev-Style ESLint Config: https://github.com/NullDev/JavaScript-Styleguide
5 | "plugins": [], // Additional ESLint Plugins
6 | "env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments
7 | "browser": true, // browser global variables
8 | "node": true, // Node.js global variables and Node.js-specific rules
9 | "commonjs": true, // CommonJS global variables and CommonJS scoping
10 | "es2022": true, // ESNext support
11 | "es6": true // enable all ECMAScript 6 features
12 | },
13 | "parser": "@babel/eslint-parser", // npm install @babel/eslint-parser @babel/core eslint --save-dev
14 | "parserOptions": {
15 | "ecmaVersion": "latest", // set highest possible version
16 | "sourceType": "module", // prefer ES Modules (doesn't require "use strict")
17 | "requireConfigFile": false, // make babel not look for config
18 | "babelOptions": {
19 | "plugins": [ // additional plugins for new ES-proposals such as "@babel/plugin-proposal-class-properties"
20 | ]
21 | }
22 | },
23 | "ignorePatterns": [ // Ignore dist folders and dependencies
24 | "dist",
25 | "node_modules"
26 | ],
27 | "rules": {
28 | /**
29 | * Strict mode
30 | */
31 | "strict": [2, "global"], // http://eslint.org/docs/rules/strict
32 | /**
33 | * ES6
34 | */
35 | "no-var": 2, // http://eslint.org/docs/rules/no-var
36 | "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const
37 | "prefer-destructuring": [2, { // http://eslint.org/docs/rules/prefer-destructuring
38 | "array": false,
39 | "object": true
40 | }, {
41 | "enforceForRenamedProperties": false
42 | }],
43 | /**
44 | * Variables
45 | */
46 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
47 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
48 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
49 | "vars": "local",
50 | "args": "after-used"
51 | }],
52 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
53 | /**
54 | * Possible errors
55 | */
56 | "comma-dangle": [ // http://eslint.org/docs/rules/comma-dangle
57 | 2,
58 | "always-multiline"
59 | ],
60 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
61 | "no-console": 0, // http://eslint.org/docs/rules/no-console
62 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
63 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert
64 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
65 | "no-const-assign": 2, // http://eslint.org/docs/rules/no-const-assign
66 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
67 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
68 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty
69 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
70 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
71 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
72 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
73 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
74 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
75 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
76 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
77 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
78 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
79 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
80 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
81 | "valid-typeof": 2, // http://eslint.org/docs/rules/valid-typeof
82 | /**
83 | * Best practices
84 | */
85 | "array-callback-return": [2, { // http://eslint.org/docs/rules/array-callback-return
86 | "allowImplicit": true
87 | }],
88 | "consistent-return": 1, // http://eslint.org/docs/rules/consistent-return
89 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly
90 | "default-case": 2, // http://eslint.org/docs/rules/default-case
91 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
92 | "allowKeywords": true
93 | }],
94 | "linebreak-style": [2, "unix"], // http://eslint.org/docs/rules/linebreak-style
95 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq
96 | "guard-for-in": 0, // http://eslint.org/docs/rules/guard-for-in
97 | "no-array-constructor": 2, // http://eslint.org/docs/rules/no-array-constructor
98 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller
99 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
100 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
101 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval
102 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
103 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
104 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
105 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
106 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
107 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
108 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
109 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
110 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
111 | "no-new": 2, // http://eslint.org/docs/rules/no-new
112 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
113 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
114 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal
115 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
116 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
117 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto
118 | "no-prototype-builtins": 1, // http://eslint.org/docs/rules/no-prototype-builtins
119 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
120 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
121 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
122 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
123 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
124 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
125 | "no-with": 2, // http://eslint.org/docs/rules/no-with
126 | "radix": 2, // http://eslint.org/docs/rules/radix
127 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
128 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
129 | "object-shorthand": [2, "always", { // http://eslint.org/docs/rules/object-shorthand
130 | "ignoreConstructors": true,
131 | "avoidQuotes": true
132 | }],
133 | "quote-props": [2, "as-needed", { // http://eslint.org/docs/rules/quote-props
134 | "keywords": true
135 | }],
136 | "yoda": 2, // http://eslint.org/docs/rules/yoda
137 | /**
138 | * Style
139 | */
140 | "indent": [2, 4, { // http://eslint.org/docs/rules/indent
141 | "SwitchCase": 1
142 | }],
143 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style
144 | "stroustrup", {
145 | "allowSingleLine": true
146 | }
147 | ],
148 | "quotes": [
149 | 2, "double", "avoid-escape" // http://eslint.org/docs/rules/quotes
150 | ],
151 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase
152 | "properties": "never"
153 | }],
154 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
155 | "before": false,
156 | "after": true
157 | }],
158 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
159 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last
160 | "func-names": 0, // http://eslint.org/docs/rules/func-names
161 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
162 | "beforeColon": false,
163 | "afterColon": true
164 | }],
165 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap
166 | "newIsCap": true
167 | }],
168 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
169 | "max": 2
170 | }],
171 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
172 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
173 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
174 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
175 | "no-extra-parens": [2,
176 | "functions" // http://eslint.org/docs/rules/no-extra-parens
177 | ],
178 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
179 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
180 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
181 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi
182 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
183 | "before": false,
184 | "after": true
185 | }],
186 | "space-after-keywords": 0, // http://eslint.org/docs/rules/space-after-keywords
187 | "space-before-blocks": [2, { // http://eslint.org/docs/rules/space-before-blocks
188 | "functions": "never",
189 | "keywords": "never",
190 | "classes": "always"
191 | }],
192 | "keyword-spacing": [0, { // http://eslint.org/docs/rules/keyword-spacing
193 | "before": false,
194 | "after": true
195 | }],
196 | "space-before-function-paren": [2,
197 | "never" // http://eslint.org/docs/rules/space-before-function-paren
198 | ],
199 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
200 | "space-return-throw-case": 0, // http://eslint.org/docs/rules/space-return-throw-case
201 | "spaced-comment": 2 // http://eslint.org/docs/rules/spaced-comment
202 | }
203 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2012 Airbnb
4 | Copyright (c) 2018-2021 NullDev
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README-German-DE.md:
--------------------------------------------------------------------------------
1 | [](https://nulldev.org)
2 |
3 | # NullDev JavaScript StyleGuide auf Deutsch (WIP)
4 |
5 |

6 | Ein zumeist vernünftiger Ansatz für JavaScript
7 | Oder... Hauptsächlich NodeJS...
8 |
9 |
10 |
11 | ## Inhalt
12 |
13 | 1. :clipboard: [Typen](#types)
14 |
15 | Inhalte anzeigen
16 |
17 | - [1.1](#types--primitives) Primitive Typen
18 | - [1.2](#types--complex) Komplexe Typen
19 |
20 |
21 |
22 | 1. :link: [Referenzen](#references)
23 |
24 | Inhalte anzeigen
25 |
26 | - [2.1](#references--prefer-const) `const` bevorzugen
27 | - [2.2](#references--disallow-var) `var` verbieten
28 | - [2.3](#references--let-require) `let` für `require()`
29 | - [2.4](#references--block-scope) Block scope
30 |
31 |
32 |
33 | 1. :package: [Objekte](#objects)
34 |
35 | Inhalte anzeigen
36 |
37 | - [3.1](#objects--no-new) Literals
38 | - [3.2](#es6-computed-properties) Computed properties
39 | - [3.3](#es6-object-shorthand) Objekt Kurzform
40 | - [3.4](#es6-object-concise) Object concise
41 | - [3.5](#objects--grouped-shorthand) Grouped shorthand
42 | - [3.6](#objects--quoted-props) Quoted properties
43 | - [3.7](#objects--prototype-builtins) Prototype Builtins
44 | - [3.8](#objects--rest-spread) Rest spread
45 |
46 |
47 |
48 | 1. :bookmark_tabs: [Arrays](#arrays)
49 |
50 | Inhalte anzeigen
51 |
52 | - [4.1](#arrays--literals) Literale
53 | - [4.2](#arrays--push) Push
54 | - [4.3](#es6-array-spreads) Array spreads
55 | - [4.4](#arrays--from-iterable) spread für iterable
56 | - [4.5](#arrays--from-array-like) Array-ähnliche Objekte
57 | - [4.6](#arrays--mapping) Mapping
58 | - [4.7](#arrays--callback-return) Callback return
59 | - [4.8](#arrays--bracket-newline) Zeilenumbrüche
60 |
61 |
62 |
63 | 1. :hammer: [Destructuring](#destructuring)
64 |
65 | Inhalte anzeigen
66 |
67 | - [5.1](#destructuring--object) Object destructuring
68 | - [5.2](#destructuring--array) Array destructuring
69 | - [5.3](#destructuring--object-over-array) Object over array
70 |
71 |
72 |
73 | 1. :page_facing_up: [Strings](#strings)
74 |
75 | Inhalte anzeigen
76 |
77 | - [6.1](#strings--quotes) Quotes
78 | - [6.2](#strings--line-length) Length
79 | - [6.3](#es6-template-literals) Template literals
80 | - [6.4](#strings--eval) Eval
81 | - [6.5](#strings--escaping) Escaping
82 |
83 |
84 |
85 | 1. :pager: [Functions](#functions)
86 |
87 | Inhalte anzeigen
88 |
89 | - [7.1](#functions--use-strict) Strict mode
90 | - [7.2](#functions--declarations) Deklarationen
91 | - [7.3](#functions--iife) IIFE's
92 | - [7.4](#functions--in-blocks) Nicht-Funktionsblöcke
93 | - [7.5](#functions--note-on-blocks) Blöcke (Info)
94 | - [7.6](#functions--arguments-shadow) Arguments shadow
95 | - [7.7](#es6-rest) Rest
96 | - [7.8](#es6-default-parameters) Standard-Parameter
97 | - [7.9](#functions--default-side-effects) Side effects
98 | - [7.10](#functions--defaults-last) Standardwerte zuletzt
99 | - [7.11](#functions--constructor) Funktionskonstruktor
100 | - [7.12](#functions--signature-spacing) Signature spacing
101 | - [7.13](#functions--mutate-params) Mutating parameters
102 | - [7.14](#functions--reassign-params) Neuzuweisung von Parametern
103 | - [7.15](#functions--spread-vs-apply) Spread
104 | - [7.16](#functions--signature-invocation-indentation) Signaturaufrufseinrückungen
105 |
106 |
107 |
108 | 1. :arrow_right: [Arrow Functions](#arrow-functions)
109 |
110 | Inhalte anzeigen
111 |
112 | - [8.1](#arrows--use-them) Verwendung
113 | - [8.2](#arrows--implicit-return) Implizite Rückgaben
114 | - [8.3](#arrows--paren-wrap) Wrap
115 | - [8.4](#arrows--one-arg-parens) Weglassen von Klammern
116 | - [8.5](#arrows--confusing) Verwirrende Funktionen
117 | - [8.6](#whitespace--implicit-arrow-linebreak) Zeilenumbrüche
118 |
119 |
120 |
121 | 1. :triangular_ruler: [Classes & Constructors](#classes--constructors)
122 |
123 | Inhalte anzeigen
124 |
125 | - [9.1](#constructors--use-class) `class` verwenden
126 | - [9.2](#constructors--extends) Vererbung
127 | - [9.3](#constructors--chaining) Verkettung
128 | - [9.4](#constructors--tostring) toString Methoden
129 | - [9.5](#constructors--no-useless) Keine leeren Konstruktoren
130 | - [9.6](#classes--no-duplicate-members) Keine redundanten member
131 |
132 |
133 |
134 | 1. :postbox: [Modules](#modules)
135 |
136 | Inhalte anzeigen
137 |
138 | - [10.1](#modules--use-them) CommonJS
139 | - [10.2](#modules--no-duplicate-imports) Redundante imports
140 | - [10.3](#modules--no-mutable-exports) Veränderbare exports
141 | - [10.4](#modules--imports-first) Imports zuerst
142 | - [10.5](#modules--multiline-imports-over-newlines) Mehrzeilige imports
143 |
144 |
145 |
146 | 1. :arrows_clockwise: [Iterators and Generators](#iterators-and-generators)
147 |
148 | Inhalte anzeigen
149 |
150 | - [11.1](#iterators--nope) Higher-order functions
151 | - [11.2](#generators--nope) Generators
152 | - [11.3](#generators--spacing) Spacing
153 |
154 |
155 |
156 | 1. :bookmark_tabs: [Properties](#properties)
157 |
158 | Inhalte anzeigen
159 |
160 | - [12.1](#properties--dot) Punkt Notation
161 | - [12.2](#properties--bracket) Klammer Notation
162 | - [12.3](#es2016-properties--exponentiation-operator) Exponenzial operator
163 |
164 |
165 |
166 | 1. :floppy_disk: [Variables](#variables)
167 |
168 | Inhalte anzeigen
169 |
170 | - [13.1](#variables--const) Globaler Namespace
171 | - [13.2](#variables--one-const) Deklaration per Variabel
172 | - [13.3](#variables--const-let-group) Gruppierung
173 | - [13.4](#variables--define-where-used) Platzierung
174 | - [13.5](#variables--no-chain-assignment) Ketten-Zuweisung
175 | - [13.6](#variables--unary-increment-decrement) Inkrementierung & Dekrementierung
176 | - [13.7](#variables--linebreak) Zeilenumbrüche
177 | - [13.8](#variables--no-unused-vars) Nicht benutzte Variablen
178 | - [13.9](#variables--bitwise-floor) Bitwise floor
179 |
180 |
181 |
182 | 1. :arrow_heading_up: [Hoisting](#hoisting)
183 |
184 | Inhalte anzeigen
185 |
186 | - [14.1](#hoisting--about) About
187 | - [14.2](#hoisting--anon-expressions) Anonymous function expressions
188 | - [14.3](#hoisting--named-expressions) Named function expressions
189 | - [14.4](#hoisting--declarations) Function declarations
190 |
191 |
192 |
193 | 1. :left_right_arrow: [Comparison Operators & Equality](#comparison-operators--equality)
194 |
195 | Inhalte anzeigen
196 |
197 | - [15.1](#comparison--eqeqeq) Strict comparison
198 | - [15.2](#comparison--if) Conditional statements
199 | - [15.3](#comparison--shortcuts) Shortcuts
200 | - [15.4](#comparison--moreinfo) Truth Equality
201 | - [15.5](#comparison--switch-blocks) Switch blocks
202 | - [15.6](#comparison--nested-ternaries) Nested ternaries
203 | - [15.7](#comparison--unneeded-ternary) Unneeded-ternaries
204 | - [15.8](#comparison--no-mixed-operators) Mixed operators
205 |
206 |
207 |
208 | 1. :black_square_button: [Blocks](#blocks)
209 |
210 | Inhalte anzeigen
211 |
212 | - [16.1](#blocks--braces) Braces
213 | - [16.2](#blocks--cuddled-elses) Cuddled elses
214 | - [16.3](#blocks--no-else-return) Returns
215 |
216 |
217 |
218 | 1. :wrench: [Control Statements](#control-statements)
219 |
220 | Inhalte anzeigen
221 |
222 | - [17.1](#control-statements) Length
223 | - [17.2](#control-statements--value-selection) Selection operators
224 |
225 |
226 |
227 | 1. :pencil: [Comments](#comments)
228 |
229 | Inhalte anzeigen
230 |
231 | - [18.1](#comments--multiline) Multi-line
232 | - [18.2](#comments--singleline) Single-line
233 | - [18.3](#comments--spaces) Spaces
234 | - [18.4](#comments--actionitems) Prefixing / Action-items
235 | - [18.5](#comments--fixme) FixMe
236 | - [18.6](#comments--todo) ToDo
237 |
238 |
239 |
240 | 1. :white_circle: [Whitespace](#whitespace)
241 |
242 | Inhalte anzeigen
243 |
244 | - [19.1](#whitespace--spaces) Soft tabs
245 | - [19.2](#whitespace--before-blocks) Before blocks
246 | - [19.3](#whitespace--around-keywords) Around keywords
247 | - [19.4](#whitespace--infix-ops) Infix operators
248 | - [19.5](#whitespace--newline-at-end) End of file
249 | - [19.6](#whitespace--chains) Chains
250 | - [19.7](#whitespace--after-blocks) After blocks
251 | - [19.8](#whitespace--padded-blocks) Padded blocks
252 | - [19.9](#whitespace--in-parens) Inside parentheses
253 | - [19.10](#whitespace--in-brackets) Inside brackets
254 | - [19.11](#whitespace--in-braces) Inside curly braces
255 | - [19.12](#whitespace--max-len) Line length
256 | - [19.13](#whitespace--block-spacing) Block spacing
257 | - [19.14](#whitespace--comma-spacing) Comma spacing
258 | - [19.15](#whitespace--computed-property-spacing) Computed properties
259 | - [19.17](#whitespace--key-spacing) Key spacing
260 | - [19.18](#whitespace--no-trailing-spaces) Trailing spaces
261 | - [19.19](#whitespace--no-multiple-empty-lines) Multiple empty lines
262 |
263 |
264 |
265 | 1. :small_red_triangle_down: [Commas](#commas)
266 |
267 | Inhalte anzeigen
268 |
269 | - [20.1](#commas--leading-trailing) Leading commas
270 | - [20.2](#commas--dangling) Comma Dangling
271 |
272 |
273 |
274 | 1. :heavy_exclamation_mark: [Semicolons](#semicolons)
275 |
276 | Inhalte anzeigen
277 |
278 | - [21.1](#semicolons--required) Use semicolons
279 |
280 |
281 |
282 | 1. :twisted_rightwards_arrows: [Type Casting & Coercion](#type-casting--coercion)
283 |
284 | Inhalte anzeigen
285 |
286 | - [22.1](#coercion--explicit) Explicit coercion
287 | - [22.2](#coercion--strings) Strings
288 | - [22.3](#coercion--numbers) Numbers
289 | - [22.4](#coercion--comment-deviations) Deviations
290 | - [22.5](#coercion--bitwise) Bitwise
291 | - [22.6](#coercion--booleans) Booleans
292 |
293 |
294 |
295 | 1. :scroll: [Naming Conventions](#naming-conventions)
296 |
297 | Inhalte anzeigen
298 |
299 | - [23.1](#naming--descriptive) Beschreibend
300 | - [23.2](#naming--camelCase) camelCase
301 | - [23.3](#naming--PascalCase) PascalCase
302 | - [23.4](#naming--leading-underscore) Unterstriche
303 | - [23.5](#naming--self-this) `this`
304 | - [23.6](#naming--filename-matches-export) Dateinamen
305 | - [23.7](#naming--camelCase-default-export) Standard export
306 | - [23.8](#naming--PascalCase-singleton) Singleton
307 | - [23.9](#naming--Acronyms-and-Initialisms) Akronyme und Inizialisierungen
308 | - [23.10](#naming--uppercase) Großbuchstaben
309 |
310 |
311 |
312 | 1. :arrow_down: [Accessors](#accessors)
313 |
314 | Inhalte anzeigen
315 |
316 | - [24.1](#accessors--not-required) Not required accessors
317 | - [24.2](#accessors--no-getters-setters) Getters & Setters
318 | - [24.3](#accessors--boolean-prefix) Boolean prefix
319 | - [24.4](#accessors--consistent) Consistency
320 |
321 |
322 |
323 | 1. :high_brightness: [Events](#events)
324 |
325 | Inhalte anzeigen
326 |
327 | - [25.1](#events--hash) Hash
328 |
329 |
330 |
331 | 1. :book: [Standard Library](#standard-library)
332 |
333 | Inhalte anzeigen
334 |
335 | - [26.1](#standard-library--isnan) isNaN
336 | - [26.2](#standard-library--isfinite) isFinite
337 |
338 |
339 |
340 | 1. :nut_and_bolt: [jQuery](#jquery)
341 |
342 | Inhalte anzeigen
343 |
344 | - [27.1](#jquery--dollar-prefix) Prefix
345 | - [27.2](#jquery--cache) Cache
346 | - [27.3](#jquery--queries) DOM Abfragen
347 | - [27.4](#jquery--find) Find
348 | - [27.5](#jquery--on) Shorthands
349 |
350 |
351 |
352 | 1. :arrows_counterclockwise: [ECMAScript 5 Kompartibilität](#ecmascript-5-compatibility)
353 |
354 | Inhalte anzeigen
355 |
356 | - [28.1](#es5-compat--kangax) Kompartibilitäts Tabelle
357 |
358 |
359 |
360 | 1. :six: [ECMAScript 6+ (ES 2015+) Styles](#ecmascript-6-es-2015-styles)
361 |
362 | Inhalte anzeigen
363 |
364 | - [29.1](#es6-styles) ES6 Styles
365 | - [29.2](#tc39-proposals) Proposals
366 |
367 |
368 |
369 | 1. :electric_plug: [Testen](#testing)
370 |
371 | Inhalte anzeigen
372 |
373 | - [30.1](#testing--tests) Tests
374 |
375 |
376 |
377 | 1. :chart_with_upwards_trend: [Performance](#performance)
378 |
379 | Inhalte anzeigen
380 |
381 | - [31.1](#performance--performance) Performance
382 |
383 |
384 |
385 | 1. :books: [Resourcen](#resources)
386 |
387 | Inhalte anzeigen
388 |
389 | - [32.1](#resources--learning-es6) ES6+ lernen
390 | - [32.2](#resources--read-this) Zum lesen
391 | - [32.3](#resources--tools) Tools
392 | - [32.4](#resources--further-reading) Mehr zum lesen
393 | - [32.5](#resources--books) Bücher
394 | - [32.6](#resources--blogs) Blogs
395 | - [32.7](#resources--podcasts) Podcasts
396 |
397 |
398 |
399 | 1. :copyright: [Copyright](#copyright)
400 |
401 | Inhalte anzeigen
402 |
403 | - [33.1](#copyright--base) Basis
404 | - [33.2](#copyright--license) Lizenz
405 | - [33.3](#copyright--german) Englisch
406 |
407 |
408 |
409 | 1. :recycle: [Änderungen](#amendments)
410 |
411 | Inhalte anzeigen
412 |
413 | - [34.1](#amendments--forking) Forking
414 |
415 |
416 |
417 | ## Typen
418 |
419 |
420 | - [1.1](#types--primitives) **Primitive Typen**: Bei primitiven Datentypen wird immer direkt auf deren Wert zugegriffen.
421 |
422 | - `string`
423 | - `number`
424 | - `boolean`
425 | - `null`
426 | - `undefined`
427 | - `symbol`
428 |
429 | ```javascript
430 | const foo = 1;
431 | let bar = foo;
432 |
433 | bar = 9;
434 |
435 | console.log(foo, bar); // => 1, 9
436 | ```
437 |
438 | - Symbols können oft nicht richtig polyfilled werden. Deshalb sollte man sie vermeiden, wenn man für Browserumgebungen entwickelt, die diese nicht nativ unterstützen.
439 |
440 |
441 | - [1.2](#types--complex) **Komplexe Typen**: Bei komplexen Datentypen wird immer auf eine Referenz zugegriffen.
442 |
443 | - `object`
444 | - `array`
445 | - `function`
446 |
447 | ```javascript
448 | const foo = [1, 2];
449 | const bar = foo;
450 |
451 | bar[0] = 9;
452 |
453 | console.log(foo[0], bar[0]); // => 9, 9
454 | ```
455 |
456 | **[⬆ zurück zum Anfang](#table-of-contents)**
457 |
458 | ## Referenzen
459 |
460 |
461 | - [2.1](#references--prefer-const) `const` für alle Referenzen benutzen; Die Verwendung von `var` vermeiden. eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign.html)
462 |
463 | > Wieso? Es stellt sicher, dass man Referenzen nicht neu zuweisen kann. Das kann sonst zu Fehlern und schwer verständlichem Code führen.
464 |
465 | ```javascript
466 | // schlecht
467 | var a = 1;
468 | var b = 2;
469 |
470 | // gut
471 | const a = 1;
472 | const b = 2;
473 | ```
474 |
475 |
476 | - [2.2](#references--disallow-var) Wenn man eine Referenz neu zuweisen muss, sollte `let` an Stelle von `var` benutzt werden. eslint: [`no-var`](https://eslint.org/docs/rules/no-var.html)
477 |
478 | > Wieso? `let` ist block-scoped und nicht function-scoped wie `var`.
479 |
480 | ```javascript
481 | // schlecht
482 | var count = 1;
483 | if (true){
484 | count += 1;
485 | }
486 |
487 | // gut
488 | let count = 1;
489 | if (true){
490 | count += 1;
491 | }
492 | ```
493 |
494 |
495 | - [2.3](#references--let-require) `let` für `require()` imports in NodeJS benutzen.
496 |
497 | > Wieso? Mit `const` kann man die Import-Variable später nicht neu definieren falls nötig. Außerdemn kann die Variable nicht verwendet werden, ohne sie explizit zu inizialisieren.
498 |
499 | ```javascript
500 | // schlecht
501 | var x = require("x");
502 | const y = require("y");
503 |
504 | // gut
505 | let x = require("x");
506 | let y = require("y");
507 | ```
508 |
509 |
510 | - [2.4](#references--block-scope) Beachten, dass `let` und `const` block-scoped sind.
511 |
512 | ```javascript
513 | // const und let existieren nur in dem Block, in dem sie definiert wurden.
514 | {
515 | let a = 1;
516 | const b = 1;
517 | var c = 1;
518 | }
519 | console.log(a); // ReferenceError: a ist nicht definiert
520 | console.log(b); // ReferenceError: b ist nicht definiert
521 | console.log(c); // 1
522 | ```
523 |
524 | **[⬆ zurück zum Anfang](#table-of-contents)**
525 |
526 | ## Objekte
527 |
528 |
529 | - [3.1](#objects--no-new) "literal syntax" für die Erstellung von Objekten benutzen. eslint: [`no-new-object`](https://eslint.org/docs/rules/no-new-object.html)
530 |
531 | ```javascript
532 | // schlecht
533 | const item = new Object();
534 |
535 | // gut
536 | const item = {};
537 | ```
538 |
539 |
540 | - [3.2](#es6-computed-properties) Automatisch berechnete Eigenschaftsnamen verwenden, wenn Objekte mit dynamischen Eigenschaftennamen erstellt werden.
541 |
542 | > Wieso? Sie erlauben es, alle Eigenschaften eines Objekts an einer Stelle zu definieren.
543 |
544 | ```javascript
545 |
546 | function getKey(k){
547 | return `ein key mit dem Namen ${k}`;
548 | }
549 |
550 | // schlecht
551 | const obj = {
552 | id: 5,
553 | name: "foo",
554 | };
555 | obj[getKey("enabled")] = true;
556 |
557 | // gut
558 | const obj = {
559 | id: 5,
560 | name: "foo",
561 | [getKey("enabled")]: true,
562 | };
563 | ```
564 |
565 |
566 | - [3.3](#es6-object-shorthand) Objekt-Methoden-Shorthands verwenden. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
567 |
568 | ```javascript
569 | // schlecht
570 | const foo = {
571 | value: 1,
572 |
573 | addValue: function (value) {
574 | return foo.value + value;
575 | },
576 | };
577 |
578 | // gut
579 | const foo = {
580 | value: 1,
581 |
582 | addValue(value) {
583 | return foo.value + value;
584 | },
585 | };
586 | ```
587 |
588 |
589 | - [3.4](#es6-object-concise) Eigenschaftsnamen-Shorthands benutzen. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
590 |
591 | > Wieso? Es ist kürzer und funktionsbeschreibend.
592 |
593 | ```javascript
594 | const foo = "bar";
595 |
596 | // schlecht
597 | const obj = {
598 | foo: foo,
599 | };
600 |
601 | // gut
602 | const obj = {
603 | foo,
604 | };
605 | ```
606 |
607 |
608 | - [3.5](#objects--grouped-shorthand) Shorthand Eigenschaften am Anfang der Objekt Deklaration gruppieren.
609 |
610 | > Wieso? Man kann einfacher und schneller erkennen, welche Eigenschaften den Shorthand verwenden.
611 |
612 | ```javascript
613 | const foo = "bar";
614 | const bar = "foo";
615 |
616 | // schlecht
617 | const obj = {
618 | test: 1,
619 | key: 2,
620 | foo,
621 | abc: 3,
622 | xyz: 4,
623 | bar,
624 | };
625 |
626 | // gut
627 | const obj = {
628 | foo,
629 | bar,
630 | test: 1,
631 | key: 2,
632 | abc: 3,
633 | xyz: 4,
634 | };
635 | ```
636 |
637 |
638 | - [3.6](#objects--quoted-props) Nur Properties unter Anführungszeichen setzen, die invalide identifier darstellen würden. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html)
639 |
640 | > Warum? Im Allgemeinen ist es subjektiv einfacher zu lesen. Es verbessert die Syntaxhervorhebung und wird auch von vielen JS-Engines leichter optimiert.
641 |
642 | ```javascript
643 | // schlecht
644 | const bad = {
645 | "foo": 3,
646 | "bar": 4,
647 | "data-foo": 5
648 | };
649 |
650 | // gut
651 | const good = {
652 | foo: 3,
653 | bar: 4,
654 | "data-foo": 5
655 | };
656 | ```
657 |
658 |
659 | - [3.7](#objects--prototype-builtins) `Object.prototype` Methoden wie `hasOwnProperty`, `propertyIsEnumerable`, und `isPrototypeOf` niemals direkt aufrufen. eslint: [`no-prototype-builtins`](https://eslint.org/docs/rules/no-prototype-builtins)
660 |
661 | > Warum? Diese Methoden könnten durch Eigenschaften des Ursprungsobjektes "shadowed" werden - z.B. `{ hasOwnProperty: false }` - oder, das Objekt könnte ein null-Objekt sein (`Object.create(null)`).
662 |
663 | ```javascript
664 | // schlecht
665 | object.hasOwnProperty(key);
666 |
667 | // gut
668 | Object.prototype.hasOwnProperty.call(object, key);
669 |
670 | // am besten - Stage 3 Proposal:
671 | // https://github.com/tc39/proposal-accessible-object-hasownproperty
672 | Object.hasOwn(object, key);
673 | ```
674 |
675 |
676 | - [3.8](#objects--rest-spread) Den Objekt "spread" Operator an Stelle von [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) bevorzugen, um shallow-copy's von Objekten zu machen. Den Objekt "rest" Operator verwenden, um ein neues Objekt zu erzeugen, bei dem gewissen Eigenschaften weggelassen werden.
677 |
678 | ```javascript
679 | // sehr schlecht
680 | const original = { a: 1, b: 2 };
681 | const copy = Object.assign(original, { c: 3 }); // dies modifiziert `original`
682 | delete copy.a; // das auch
683 |
684 | // schlecht
685 | const original = { a: 1, b: 2 };
686 | const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
687 |
688 | // gut
689 | const original = { a: 1, b: 2 };
690 | const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
691 |
692 | const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
693 | ```
694 |
695 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
696 |
697 | ## Copyright
698 |
699 |
700 | - [33.1](#copyright--base) Dieser Styleguide basiert auf dem [AirBnB JavaScript Styleguide](https://github.com/airbnb/javascript)
701 |
702 |
703 | - [33.2](#copyright--license) Der Styleguide steht unter der selben [Lizenz](https://github.com/NullDevCo/JavaScript-Styleguide/blob/master/LICENSE).
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 | [](https://nulldev.org)
714 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://nulldev.org)
2 |
3 | # NullDev JavaScript StyleGuide
4 |
5 | ✨ _**Now with the new ESLint Flat Config Format!**_ ✨
6 |
7 | 
8 | A mostly reasonable approach to JavaScript
9 | Or... Mainly NodeJS...
10 |
11 |
12 |
13 | ---
14 |
15 | ## Setup :gear:
16 |
17 | ### To integrate into your own project:
18 |
19 | - Make sure ESLint is installed. For VSCode, [download the extension here](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
20 | - Navigate inside your project
21 | - Install [Babel-ESLint](https://www.npmjs.com/package/@babel/eslint-parser)*:
22 | $ `npm i -D @babel/eslint-parser @babel/core eslint @types/node`
23 | Or with Bun:
24 | $ `bun i -D @babel/eslint-parser @babel/core eslint @types/bun`
25 | - Get the config:
26 | $ `wget https://raw.githubusercontent.com/NullDev/JavaScript-Styleguide/master/eslint.config.js`
27 | - Done! :)
28 |
29 | *) The rationale for using Babel-ESLint is that it supports the newest Stage-3 ECMAScript features.
30 |
31 | ### Or use the [NullDev Project Template](https://github.com/NullDev/JavaScript-Styleguide/tree/master/nulldev-template) ...
32 |
33 | ... with already preset config files and project structure
34 |
35 | [DOWNLOAD](https://downgit.github.io/#/home?url=https://github.com/NullDev/JavaScript-Styleguide/tree/master/nulldev-template)
36 |
37 | $ `npm i` - and you're ready to go!
38 |
39 | ---
40 |
41 | ## Table of Contents :bookmark_tabs:
42 |
43 | 1. :clipboard: [Types](#types)
44 |
45 | View contents
46 |
47 | - [1.1](#types--primitives) Primitives
48 | - [1.2](#types--complex) Complex
49 |
50 |
51 |
52 | 1. :link: [References](#references)
53 |
54 | View contents
55 |
56 | - [2.1](#references--prefer-const) Prefer `const`
57 | - [2.2](#references--disallow-var) Disallow `var`
58 | - [2.3](#references--block-scope) Block scope
59 |
60 |
61 |
62 | 1. :package: [Objects](#objects)
63 |
64 | View contents
65 |
66 | - [3.1](#objects--no-new) Literals
67 | - [3.2](#es6-computed-properties) Computed properties
68 | - [3.3](#es6-object-shorthand) Object shorthand
69 | - [3.4](#es6-object-concise) Object concise
70 | - [3.5](#objects--grouped-shorthand) Grouped shorthand
71 | - [3.6](#objects--quoted-props) Quoted properties
72 | - [3.7](#objects--prototype-builtins) Prototype Builtins
73 | - [3.8](#objects--rest-spread) Rest spread
74 |
75 |
76 |
77 | 1. :bookmark_tabs: [Arrays](#arrays)
78 |
79 | View contents
80 |
81 | - [4.1](#arrays--literals) Literals
82 | - [4.2](#arrays--push) Push
83 | - [4.3](#es6-array-spreads) Array spreads
84 | - [4.4](#arrays--from-iterable) spread for iterable
85 | - [4.5](#arrays--from-array-like) Array-like objects
86 | - [4.6](#arrays--mapping) Mapping
87 | - [4.7](#arrays--callback-return) Callback return
88 | - [4.8](#arrays--bracket-newline) Linebreaks
89 |
90 |
91 |
92 | 1. :hammer: [Destructuring](#destructuring)
93 |
94 | View contents
95 |
96 | - [5.1](#destructuring--object) Object destructuring
97 | - [5.2](#destructuring--array) Array destructuring
98 | - [5.3](#destructuring--object-over-array) Object over array
99 |
100 |
101 |
102 | 1. :page_facing_up: [Strings](#strings)
103 |
104 | View contents
105 |
106 | - [6.1](#strings--quotes) Quotes
107 | - [6.2](#strings--line-length) Length
108 | - [6.3](#es6-template-literals) Template literals
109 | - [6.4](#strings--eval) Eval
110 | - [6.5](#strings--escaping) Escaping
111 | - [6.6](#strings--regex) Regular Expressions (RegEx)
112 |
113 |
114 |
115 | 1. :pager: [Functions](#functions)
116 |
117 | View contents
118 |
119 | - [7.1](#functions--use-strict) Strict mode
120 | - [7.2](#functions--declarations) Declarations
121 | - [7.3](#functions--iife) IIFE's
122 | - [7.4](#functions--in-blocks) Non-function blocks
123 | - [7.5](#functions--note-on-blocks) Blocks (Info)
124 | - [7.6](#functions--arguments-shadow) Arguments shadow
125 | - [7.7](#es6-rest) Rest
126 | - [7.8](#es6-default-parameters) Default parameters
127 | - [7.9](#functions--default-side-effects) Side effects
128 | - [7.10](#functions--defaults-last) Defaults last
129 | - [7.11](#functions--constructor) Function constructor
130 | - [7.12](#functions--signature-spacing) Signature spacing
131 | - [7.13](#functions--mutate-params) Mutating parameters
132 | - [7.14](#functions--reassign-params) Reassigning parameters
133 | - [7.15](#functions--spread-vs-apply) Spread
134 | - [7.16](#functions--signature-invocation-indentation) Signature invocation indentation
135 |
136 |
137 |
138 | 1. :arrow_right: [Arrow Functions](#arrow-functions)
139 |
140 | View contents
141 |
142 | - [8.1](#arrows--use-them) Usage
143 | - [8.2](#arrows--implicit-return) Implicit return
144 | - [8.3](#arrows--paren-wrap) Wrap
145 | - [8.4](#arrows--one-arg-parens) Omitting parentheses
146 | - [8.5](#arrows--confusing) Confusing functions
147 | - [8.6](#whitespace--implicit-arrow-linebreak) Linebreaks
148 |
149 |
150 |
151 | 1. :triangular_ruler: [Classes & Constructors](#classes--constructors)
152 |
153 | View contents
154 |
155 | - [9.1](#constructors--use-class) Use `class`
156 | - [9.2](#constructors--extends) Inheritance
157 | - [9.3](#constructors--chaining) Chaining
158 | - [9.4](#constructors--tostring) toString methods
159 | - [9.5](#constructors--no-useless) No empty constructors
160 | - [9.6](#classes--no-duplicate-members) No duplicate members
161 | - [9.7](#classes--methods-use-this) `this` in Class-Methods
162 |
163 |
164 |
165 | 1. :postbox: [Modules](#modules)
166 |
167 | View contents
168 |
169 | - [10.1](#modules--use-them) CommonJS
170 | - [10.2](#modules--no-duplicate-imports) Duplicate imports
171 | - [10.3](#modules--no-mutable-exports) Mutable exports
172 | - [10.4](#modules--imports-first) Imports first
173 | - [10.5](#modules--import-extensions) Imports extensions
174 | - [10.6](#modules--multiline-imports-over-newlines) Multiline imports
175 |
176 |
177 |
178 | 1. :arrows_clockwise: [Iterators and Generators](#iterators-and-generators)
179 |
180 | View contents
181 |
182 | - [11.1](#iterators--nope) Higher-order functions
183 | - [11.2](#generators--nope) Generators
184 | - [11.3](#generators--spacing) Spacing
185 |
186 |
187 |
188 | 1. :bookmark_tabs: [Properties](#properties)
189 |
190 | View contents
191 |
192 | - [12.1](#properties--dot) Dot notation
193 | - [12.2](#properties--bracket) Bracket notation
194 | - [12.3](#es2016-properties--exponentiation-operator) Exponentiation operator
195 |
196 |
197 |
198 | 1. :floppy_disk: [Variables](#variables)
199 |
200 | View contents
201 |
202 | - [13.1](#variables--const) Global namespace
203 | - [13.2](#variables--one-const) Declaration per variable
204 | - [13.3](#variables--const-let-group) Grouping
205 | - [13.4](#variables--define-where-used) Placing
206 | - [13.5](#variables--no-chain-assignment) Chain assignment
207 | - [13.6](#variables--unary-increment-decrement) Increment & decrement
208 | - [13.7](#variables--linebreak) Linebreaks
209 | - [13.8](#variables--no-unused-vars) Unused variables
210 | - [13.9](#variables--bitwise-floor) Bitwise floor
211 |
212 |
213 |
214 | 1. :arrow_heading_up: [Hoisting](#hoisting)
215 |
216 | View contents
217 |
218 | - [14.1](#hoisting--about) About
219 | - [14.2](#hoisting--anon-expressions) Anonymous function expressions
220 | - [14.3](#hoisting--named-expressions) Named function expressions
221 | - [14.4](#hoisting--declarations) Function declarations
222 |
223 |
224 |
225 | 1. :left_right_arrow: [Comparison Operators & Equality](#comparison-operators--equality)
226 |
227 | View contents
228 |
229 | - [15.1](#comparison--eqeqeq) Strict comparison
230 | - [15.2](#comparison--if) Conditional statements
231 | - [15.3](#comparison--shortcuts) Shortcuts
232 | - [15.4](#comparison--moreinfo) Truth Equality
233 | - [15.5](#comparison--switch-blocks) Switch blocks
234 | - [15.6](#comparison--nested-ternaries) Nested ternaries
235 | - [15.7](#comparison--unneeded-ternary) Unneeded-ternaries
236 | - [15.8](#comparison--no-mixed-operators) Mixed operators
237 |
238 |
239 |
240 | 1. :black_square_button: [Blocks](#blocks)
241 |
242 | View contents
243 |
244 | - [16.1](#blocks--braces) Braces
245 | - [16.2](#blocks--cuddled-elses) Cuddled elses
246 | - [16.3](#blocks--no-else-return) Returns
247 |
248 |
249 |
250 | 1. :wrench: [Control Statements](#control-statements)
251 |
252 | View contents
253 |
254 | - [17.1](#control-statements) Length
255 | - [17.2](#control-statements--value-selection) Selection operators
256 |
257 |
258 |
259 | 1. :pencil: [Comments](#comments)
260 |
261 | View contents
262 |
263 | - [18.1](#comments--language) Language
264 | - [18.2](#comments--multiline) Multi-line
265 | - [18.3](#comments--singleline) Single-line
266 | - [18.4](#comments--spaces) Spaces
267 | - [18.5](#comments--actionitems) Prefixing / Action-items
268 | - [18.6](#comments--fixme) FixMe
269 | - [18.7](#comments--todo) ToDo
270 |
271 |
272 |
273 | 1. :white_circle: [Whitespace](#whitespace)
274 |
275 | View contents
276 |
277 | - [19.1](#whitespace--spaces) Soft tabs
278 | - [19.2](#whitespace--before-blocks) Space Before blocks
279 | - [19.3](#whitespace--around-keywords) Around keywords
280 | - [19.4](#whitespace--infix-ops) Infix operators
281 | - [19.5](#whitespace--lf-linebreaks) LF Line-breaks
282 | - [19.6](#whitespace--newline-at-end) End of file
283 | - [19.7](#whitespace--chains) Chains
284 | - [19.8](#whitespace--after-blocks) After blocks
285 | - [19.9](#whitespace--padded-blocks) Padded blocks
286 | - [19.10](#whitespace--in-parens) Inside parentheses
287 | - [19.11](#whitespace--in-brackets) Inside brackets
288 | - [19.12](#whitespace--in-braces) Inside curly braces
289 | - [19.13](#whitespace--max-len) Line length
290 | - [19.14](#whitespace--block-spacing) Block spacing
291 | - [19.15](#whitespace--comma-spacing) Comma spacing
292 | - [19.16](#whitespace--computed-property-spacing) Computed properties
293 | - [16.17](#whitespace--func-call-spacing) Function call spacing
294 | - [19.18](#whitespace--key-spacing) Key spacing
295 | - [19.19](#whitespace--no-trailing-spaces) No Trailing spaces
296 | - [19.20](#whitespace--no-multiple-empty-lines) Multiple empty lines
297 |
298 |
299 |
300 | 1. :small_red_triangle_down: [Commas](#commas)
301 |
302 | View contents
303 |
304 | - [20.1](#commas--leading-trailing) Leading commas
305 | - [20.2](#commas--dangling) Comma Dangling
306 |
307 |
308 |
309 | 1. :heavy_exclamation_mark: [Semicolons](#semicolons)
310 |
311 | View contents
312 |
313 | - [21.1](#semicolons--required) Use semicolons
314 |
315 |
316 |
317 | 1. :twisted_rightwards_arrows: [Type Casting & Coercion](#type-casting--coercion)
318 |
319 | View contents
320 |
321 | - [22.1](#coercion--explicit) Explicit coercion
322 | - [22.2](#coercion--strings) Strings
323 | - [22.3](#coercion--numbers) Numbers
324 | - [22.4](#coercion--comment-deviations) Deviations
325 | - [22.5](#coercion--bitwise) Bitwise
326 | - [22.6](#coercion--booleans) Booleans
327 | - [22.7](#coercion--valid-typeof) Valid Typeof
328 |
329 |
330 |
331 | 1. :scroll: [Naming Conventions](#naming-conventions)
332 |
333 | View contents
334 |
335 | - [23.0](#naming--language) Language
336 | - [23.1](#naming--descriptive) Descriptive
337 | - [23.2](#naming--camelCase) camelCase
338 | - [23.3](#naming--PascalCase) PascalCase
339 | - [23.4](#naming--leading-underscore) Underscores
340 | - [23.5](#naming--self-this) `this`
341 | - [23.6](#naming--filename-matches-export) Filename
342 | - [23.7](#naming--camelCase-default-export) Default export
343 | - [23.8](#naming--PascalCase-singleton) Singleton
344 | - [23.9](#naming--Acronyms-and-Initialisms) Acronyms and initialisms
345 | - [23.10](#naming--uppercase) Uppercase
346 | - [23.11](#naming--state-booleans) State-Booleans
347 |
348 |
349 |
350 | 1. :arrow_down: [Accessors](#accessors)
351 |
352 | View contents
353 |
354 | - [24.1](#accessors--not-required) Not required accessors
355 | - [24.2](#accessors--no-getters-setters) Getters & Setters
356 | - [24.3](#accessors--boolean-prefix) Boolean prefix
357 | - [24.4](#accessors--consistent) Consistency
358 |
359 |
360 |
361 | 1. :high_brightness: [Events](#events)
362 |
363 | View contents
364 |
365 | - [25.1](#events--hash) Hash
366 |
367 |
368 |
369 | 1. :book: [Standard Library](#standard-library)
370 |
371 | View contents
372 |
373 | - [26.1](#standard-library--isnan) isNaN
374 | - [26.2](#standard-library--isfinite) isFinite
375 |
376 |
377 |
378 | 1. :nut_and_bolt: [jQuery](#jquery)
379 |
380 | View contents
381 |
382 | - [27.1](#jquery--dollar-prefix) Prefix
383 | - [27.2](#jquery--cache) Cache
384 | - [27.3](#jquery--queries) DOM queries
385 | - [27.4](#jquery--find) Find
386 | - [27.5](#jquery--on) Shorthands
387 | - [27.6](#jquery--ready) Don't bloat `.ready()`
388 |
389 |
390 |
391 | 1. :arrows_counterclockwise: [ECMAScript 5 Compatibility](#ecmascript-5-compatibility)
392 |
393 | View contents
394 |
395 | - [28.1](#es5-compat--kangax) Compatibility table
396 |
397 |
398 |
399 | 1. :six: [ECMAScript 6+ (ES 2015+) Styles](#ecmascript-6-es-2015-styles)
400 |
401 | View contents
402 |
403 | - [29.1](#es6-styles) ES6 Styles
404 | - [29.2](#tc39-proposals) Proposals
405 |
406 |
407 |
408 | 1. :electric_plug: [Testing](#testing)
409 |
410 | View contents
411 |
412 | - [30.1](#testing--tests) Tests
413 |
414 |
415 |
416 | 1. :chart_with_upwards_trend: [Performance](#performance)
417 |
418 | View contents
419 |
420 | - [31.1](#performance--performance) Performance
421 |
422 |
423 |
424 | 1. :books: [Resources](#resources)
425 |
426 | View contents
427 |
428 | - [32.1](#resources--learning-es6) Learning ES6+
429 | - [32.2](#resources--read-this) Read This
430 | - [32.3](#resources--tools) Tools
431 | - [32.4](#resources--further-reading) Further Reading
432 | - [32.5](#resources--books) Books
433 | - [32.6](#resources--blogs) Blogs
434 | - [32.7](#resources--podcasts) Podcasts
435 |
436 |
437 |
438 | 1. :copyright: [Copyright](#copyright)
439 |
440 | View contents
441 |
442 | - [33.1](#copyright--base) Base
443 | - [33.2](#copyright--license) License
444 |
445 |
446 |
447 | 1. :recycle: [Amendments](#amendments)
448 |
449 | View contents
450 |
451 | - [34.1](#amendments--forking) Forking
452 |
453 |
454 |
455 | ## Types
456 |
457 |
458 | - [1.1](#types--primitives) **Primitives**: When you access a primitive type you work directly on its value.
459 |
460 | - `string`
461 | - `number`
462 | - `boolean`
463 | - `null`
464 | - `undefined`
465 | - `symbol`
466 | - `bigint`
467 |
468 | ```javascript
469 | const foo = 1;
470 | let bar = foo;
471 |
472 | bar = 9;
473 |
474 | console.log(foo, bar); // => 1, 9
475 | ```
476 |
477 | - Symbols and BigInts cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don't support them natively.
478 |
479 |
480 | - [1.2](#types--complex) **Complex**: When you access a complex type you work on a reference to its value.
481 |
482 | - `object`
483 | - `array`
484 | - `function`
485 |
486 | ```javascript
487 | const foo = [1, 2];
488 | const bar = foo;
489 |
490 | bar[0] = 9;
491 |
492 | console.log(foo[0], bar[0]); // => 9, 9
493 | ```
494 |
495 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
496 |
497 | ## References
498 |
499 |
500 | - [2.1](#references--prefer-const) Use `const` for all of your references; avoid using `var`. eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign.html)
501 |
502 | > Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
503 |
504 | ```javascript
505 | // bad
506 | var a = 1;
507 | var b = 2;
508 |
509 | // good
510 | const a = 1;
511 | const b = 2;
512 | ```
513 |
514 |
515 | - [2.2](#references--disallow-var) If you must reassign references, use `let` instead of `var`. eslint: [`no-var`](https://eslint.org/docs/rules/no-var.html)
516 |
517 | > Why? `let` is block-scoped rather than function-scoped like `var`.
518 |
519 | ```javascript
520 | // bad
521 | var count = 1;
522 | if (true){
523 | count += 1;
524 | }
525 |
526 | // good
527 | let count = 1;
528 | if (true){
529 | count += 1;
530 | }
531 | ```
532 |
533 |
534 | - [2.3](#references--block-scope) Note that both `let` and `const` are block-scoped.
535 |
536 | ```javascript
537 | // const and let only exist in the blocks they are defined in.
538 | {
539 | let a = 1;
540 | const b = 1;
541 | var c = 1;
542 | }
543 | console.log(a); // ReferenceError: a is not defined
544 | console.log(b); // ReferenceError: b is not defined
545 | console.log(c); // 1
546 | ```
547 |
548 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
549 |
550 | ## Objects
551 |
552 |
553 | - [3.1](#objects--no-new) Use the literal syntax for object creation. eslint: [`no-new-object`](https://eslint.org/docs/rules/no-new-object.html)
554 |
555 | ```javascript
556 | // bad
557 | const item = new Object();
558 |
559 | // good
560 | const item = {};
561 | ```
562 |
563 |
564 | - [3.2](#es6-computed-properties) Use computed property names when creating objects with dynamic property names.
565 |
566 | > Why? They allow you to define all the properties of an object in one place.
567 |
568 | ```javascript
569 |
570 | function getKey(k){
571 | return `a key named ${k}`;
572 | }
573 |
574 | // bad
575 | const obj = {
576 | id: 5,
577 | name: "foo",
578 | };
579 | obj[getKey("enabled")] = true;
580 |
581 | // good
582 | const obj = {
583 | id: 5,
584 | name: "foo",
585 | [getKey("enabled")]: true,
586 | };
587 | ```
588 |
589 |
590 | - [3.3](#es6-object-shorthand) Use object method shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
591 |
592 | ```javascript
593 | // bad
594 | const foo = {
595 | value: 1,
596 |
597 | addValue: function (value) {
598 | return foo.value + value;
599 | }
600 | };
601 |
602 | // good
603 | const foo = {
604 | value: 1,
605 |
606 | addValue(value) {
607 | return foo.value + value;
608 | }
609 | };
610 | ```
611 |
612 |
613 | - [3.4](#es6-object-concise) Use property value shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
614 |
615 | > Why? It is shorter and descriptive.
616 |
617 | ```javascript
618 | const foo = "bar";
619 |
620 | // bad
621 | const obj = {
622 | foo: foo,
623 | };
624 |
625 | // good
626 | const obj = {
627 | foo,
628 | };
629 | ```
630 |
631 |
632 | - [3.5](#objects--grouped-shorthand) Group your shorthand properties at the beginning of your object declaration.
633 |
634 | > Why? It’s easier to tell which properties are using the shorthand.
635 |
636 | ```javascript
637 | const foo = "bar";
638 | const bar = "foo";
639 |
640 | // bad
641 | const obj = {
642 | test: 1,
643 | key: 2,
644 | foo,
645 | abc: 3,
646 | xyz: 4,
647 | bar,
648 | };
649 |
650 | // good
651 | const obj = {
652 | foo,
653 | bar,
654 | test: 1,
655 | key: 2,
656 | abc: 3,
657 | xyz: 4,
658 | };
659 | ```
660 |
661 |
662 | - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html)
663 |
664 | > Why? In general it is subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
665 |
666 | ```javascript
667 | // bad
668 | const bad = {
669 | "foo": 3,
670 | "bar": 4,
671 | "data-foo": 5,
672 | };
673 |
674 | // good
675 | const good = {
676 | foo: 3,
677 | bar: 4,
678 | "data-foo": 5,
679 | };
680 | ```
681 |
682 |
683 | - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. eslint: [`no-prototype-builtins`](https://eslint.org/docs/rules/no-prototype-builtins)
684 |
685 | > Why? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`).
686 |
687 | ```javascript
688 | // bad
689 | object.hasOwnProperty(key);
690 |
691 | // good
692 | Object.prototype.hasOwnProperty.call(object, key);
693 |
694 | // best - Stage 3 Proposal:
695 | // https://github.com/tc39/proposal-accessible-object-hasownproperty
696 | Object.hasOwn(object, key);
697 | ```
698 |
699 |
700 | - [3.8](#objects--rest-spread) Prefer the object spread operator over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.
701 |
702 | ```javascript
703 | // very bad
704 | const original = { a: 1, b: 2 };
705 | const copy = Object.assign(original, { c: 3 }); // this mutates `original`
706 | delete copy.a; // so does this
707 |
708 | // bad
709 | const original = { a: 1, b: 2 };
710 | const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
711 |
712 | // good
713 | const original = { a: 1, b: 2 };
714 | const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
715 |
716 | const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
717 | ```
718 |
719 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
720 |
721 | ## Arrays
722 |
723 |
724 | - [4.1](#arrays--literals) Use the literal syntax for array creation. eslint: [`no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor.html)
725 |
726 | ```javascript
727 | // bad
728 | const items = new Array();
729 |
730 | // good
731 | const items = [];
732 | ```
733 |
734 |
735 | - [4.2](#arrays--push) Use [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) instead of direct assignment to add items to an array.
736 |
737 | ```javascript
738 | const someStack = [];
739 |
740 | // bad
741 | someStack[someStack.length] = "foobar";
742 |
743 | // good
744 | someStack.push("foobar");
745 | ```
746 |
747 |
748 | - [4.3](#es6-array-spreads) Use array spreads `...` to copy arrays. ([Read more...](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax))
749 |
750 | ```javascript
751 | // bad
752 | const len = items.length;
753 | const itemsCopy = [];
754 | let i;
755 |
756 | for (i = 0; i < len; i += 1){
757 | itemsCopy[i] = items[i];
758 | }
759 |
760 | // good
761 | const itemsCopy = [...items];
762 | ```
763 |
764 |
765 |
766 | - [4.4](#arrays--from-iterable) To convert an iterable object to an array, use spreads `...` instead of [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from).
767 |
768 | ```javascript
769 | const foo = document.querySelectorAll(".foo");
770 |
771 | // good
772 | const nodes = Array.from(foo);
773 |
774 | // best
775 | const nodes = [...foo];
776 | ```
777 |
778 |
779 | - [4.5](#arrays--from-array-like) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) for converting an array-like object to an array.
780 |
781 | ```javascript
782 | const arrLike = { 0: "foo", 1: "bar", 2: "baz", length: 3 };
783 |
784 | // bad
785 | const arr = Array.prototype.slice.call(arrLike);
786 |
787 | // good
788 | const arr = Array.from(arrLike);
789 | ```
790 |
791 |
792 | - [4.6](#arrays--mapping) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) instead of spread `...` for mapping over iterables, because it avoids creating an intermediate array.
793 |
794 | ```javascript
795 | // bad
796 | const baz = [...foo].map(bar);
797 |
798 | // good
799 | const baz = Array.from(foo, bar);
800 | ```
801 |
802 |
803 | - [4.7](#arrays--callback-return) Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following [8.2](#arrows--implicit-return). eslint: [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return)
804 |
805 | ```javascript
806 | // good
807 | [1, 2, 3].map((x) => {
808 | const y = x + 1;
809 | return x * y;
810 | });
811 |
812 | // good
813 | [1, 2, 3].map(x => x + 1);
814 |
815 | // bad - no returned value means `acc` becomes undefined after the first iteration
816 | [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
817 | const flatten = acc.concat(item);
818 | });
819 |
820 | // good
821 | [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
822 | const flatten = acc.concat(item);
823 | return flatten;
824 | });
825 |
826 | // bad
827 | inbox.filter((msg) => {
828 | const { subject, response } = msg;
829 | if (subject === "foo") return response === "bar";
830 | else return false;
831 | });
832 |
833 | // good
834 | inbox.filter((msg) => {
835 | const { subject, response } = msg;
836 | if (subject === "foo") return response === "bar";
837 | return false;
838 | });
839 | ```
840 |
841 |
842 | - [4.8](#arrays--bracket-newline) Use line breaks after open and before close array brackets if an array has multiple lines
843 |
844 | ```javascript
845 | // bad
846 | const arr = [[0, 1], [2, 3], [4, 5]];
847 |
848 | const objectInArray = [{
849 | id: 1,
850 | }, {
851 | id: 2,
852 | }];
853 |
854 | const numberInArray = [
855 | 1, 2, ...
856 | ];
857 |
858 | // good
859 | const arr = [
860 | [0, 1],
861 | [2, 3],
862 | [4, 5],
863 | ...
864 | ];
865 |
866 | const objectInArray = [
867 | {
868 | id: 1,
869 | },
870 | {
871 | id: 2,
872 | },
873 | ];
874 |
875 | const numberInArray = [
876 | 1,
877 | 2,
878 | ...
879 | ];
880 | ```
881 |
882 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
883 |
884 | ## Destructuring
885 |
886 |
887 | - [5.1](#destructuring--object) Use object destructuring when accessing and using multiple properties of an object. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
888 |
889 | > Why? Destructuring saves you from creating temporary references for those properties, and from repetitive access of the object. Repeating object access creates more repetitive code, requires more reading, and creates more opportunities for mistakes. Destructuring objects also provides a single site of definition of the object structure that is used in the block, rather than requiring reading the entire block to determine what is used.
890 |
891 | ```javascript
892 | // bad
893 | function getFullName(user){
894 | const firstName = user.firstName;
895 | const lastName = user.lastName;
896 |
897 | return `${firstName} ${lastName}`;
898 | }
899 |
900 | // good
901 | function getFullName(user){
902 | const { firstName, lastName } = user;
903 | return `${firstName} ${lastName}`;
904 | }
905 |
906 | // best
907 | function getFullName({ firstName, lastName }){
908 | return `${firstName} ${lastName}`;
909 | }
910 | ```
911 |
912 |
913 | - [5.2](#destructuring--array) Use array destructuring. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
914 |
915 | ```javascript
916 | const arr = [1, 2, 3, 4];
917 |
918 | // bad
919 | const first = arr[0];
920 | const second = arr[1];
921 |
922 | // good
923 | const [first, second] = arr;
924 | ```
925 |
926 |
927 | - [5.3](#destructuring--object-over-array) Use object destructuring for multiple return values, not array destructuring.
928 |
929 | > Why? You can add new properties over time or change the order of things without breaking call sites.
930 |
931 | ```javascript
932 | // bad
933 | function processInput(input){
934 | // then a miracle occurs
935 | return [left, right, top, bottom];
936 | }
937 |
938 | // the caller needs to think about the order of return data
939 | const [left, __, top] = processInput(input);
940 |
941 | // good
942 | function processInput(input){
943 | // then a miracle occurs
944 | return { left, right, top, bottom };
945 | }
946 |
947 | // the caller selects only the data they need
948 | const { left, top } = processInput(input);
949 | ```
950 |
951 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
952 |
953 | ## Strings
954 |
955 |
956 | - [6.1](#strings--quotes) Use double quotes `""` for strings instead of single quotes `''`. eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html)
957 |
958 | > Why? While other Styleguides may enforce single quotes, they mostly do it because of consistency (in favor of older projects). Here are some reasons for using double quotes:
959 | > - Double quotes eliminate the need to escape apostrophes: `"I'm"` vs `'I\'m'`.
960 | > - From a linguistic point of view, double quotes identify a passage of quoted text while single quotes are commonly used as a contraction.
961 | > - Double quotes are used to define strings in many other languages. Single quotes are used to define `char`'s in some.
962 | > - JSON Strings are only valid with double quotes.
963 |
964 | ```javascript
965 | // bad
966 | const name = 'foo bar';
967 |
968 | // bad - template literals should contain interpolation or newlines
969 | const name = `foo bar`;
970 |
971 | // good
972 | const name = "foo bar";
973 | ```
974 |
975 |
976 | - [6.2](#strings--line-length) Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.
977 |
978 | > Why? Broken strings are painful to work with and make code less searchable.
979 |
980 | ```javascript
981 | // bad
982 | const errorMessage = "This is a super long error - lorem ipsum dolor \
983 | sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget \
984 | dolor. Aenean massa. Cum sociis natoque \
985 | penatibus et.";
986 |
987 | // bad
988 | const errorMessage = "This is a super long error - lorem ipsum dolor " +
989 | "sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget " +
990 | "dolor. Aenean massa. Cum sociis natoque " +
991 | "penatibus et.";
992 |
993 | // good
994 | const errorMessage = "This is a super long error - lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et.";
995 | ```
996 |
997 |
998 | - [6.3](#es6-template-literals) When programmatically building up strings, use template strings instead of concatenation. eslint: [`prefer-template`](https://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](https://eslint.org/docs/rules/template-curly-spacing)
999 |
1000 | > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
1001 |
1002 | ```javascript
1003 | // bad
1004 | function sayHi(name){
1005 | return "How are you, " + name + "?";
1006 | }
1007 |
1008 | // very bad
1009 | function sayHi(name){
1010 | return ["How are you, ", name, "?"].join();
1011 | }
1012 |
1013 | // bad as well
1014 | function sayHi(name){
1015 | return `How are you, ${ name }?`;
1016 | }
1017 |
1018 | // good
1019 | function sayHi(name){
1020 | return `How are you, ${name}?`;
1021 | }
1022 | ```
1023 |
1024 |
1025 | - [6.4](#strings--eval) Never use `eval()` on a string, it opens too many vulnerabilities. eslint: [`no-eval`](https://eslint.org/docs/rules/no-eval)
1026 |
1027 |
1028 | - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings. eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape)
1029 |
1030 | > Why? Backslashes harm readability, thus they should only be present when necessary.
1031 |
1032 | ```javascript
1033 | // bad
1034 | const foo = '\'this\' \i\s \"quoted\"';
1035 |
1036 | // good
1037 | const foo = "\"this\" is 'quoted'";
1038 | const foo = `my name is "${name}"`;
1039 | ```
1040 |
1041 |
1042 | - [6.6](#strings--regex) Do not split regular expressions, even if some parts are used multiple times. The only exception are computed RegEx'es.
1043 |
1044 | > Why? It has a great impact on readability and can lead to extremely confusing code
1045 |
1046 | ```javascript
1047 | // bad
1048 | const baseSite = "http(?:s?):\/\/website\.com\/";
1049 | const topic = "(?:top|new|user\/\w+\/(?:uploads|likes))(?:(?:\/\w+)?)\/(\d+)";
1050 | const comment = "(?:(?::)comment(\d+))";
1051 |
1052 | const uploadsRegex = new RegExp(baseSite + topic, "gi");
1053 | const commentRegex = new RegExp(baseSite + topic + comment, "gi");
1054 | const profileRegex = new RegExp(baseSite + "user\/(\w+)", "gi");
1055 |
1056 | // good
1057 | const uploadsRegex = /http(?:s?):\/\/website\.com\/(?:top|new|user\/\w+\/(?:uploads|likes))(?:(?:\/\w+)?)\/(\d+)/gi;
1058 | const commentRegex = /http(?:s?):\/\/website\.com\/(?:top|new|user\/\w+\/(?:uploads|likes))(?:(?:\/\w+)?)\/(\d+)(?:(?::)comment(\d+))/gi;
1059 | const profileRegex = /http(?:s?):\/\/website\.com\/user\/(\w+)/gi;
1060 | ```
1061 |
1062 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
1063 |
1064 | ## Functions
1065 |
1066 |
1067 | - [7.1](#functions--use-strict) Write the `"use strict";` directive in each and every script you code and put it on the first line to scope it globally. Also, make an empty line below it. eslint: [`strict`](https://eslint.org/docs/rules/strict)
1068 |
1069 | > Why? It is a good way to make your code safer. This is because strict mode doesn't allow the usage of dangerous features which could work in a way you wouldn't expect. Some things it does:
1070 | > - It disallows non-explicit global variables.
1071 | > - Silent failing assignments will throw errors instead.
1072 | > - It requires all property names in an object literal to be unique.
1073 | > - Function parameter names must be unique as well.
1074 | >
1075 | > Also you do not need to worry about browser compartibility. It is not a statement, but a literal expression, ignored by earlier versions of JavaScript. [Read more](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) about strict mode and [why you should use it](https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript-and-what-is-the-reasoning-behind-it).
1076 | > **Note:** Modules are exempt from this rule because they always run in strict-mode.
1077 |
1078 | ```javascript
1079 | 0 // bad
1080 | 1 console.log("Foo");
1081 | 2 let x = function(){
1082 | 3 console.log("Bar");
1083 | 4 };
1084 | 5
1085 |
1086 | ---
1087 |
1088 | 0 // bad
1089 | 1 console.log("Foo");
1090 | 2 let x = function(){
1091 | 3 "use strict";
1092 | 4 // strict mode is enabled in the scope of this function only!
1093 | 5 console.log("Bar");
1094 | 6 };
1095 | 7
1096 |
1097 | ---
1098 |
1099 | 0 "use strict";
1100 | 1
1101 | 2 // ^ Good
1102 | 3 console.log("Foo");
1103 | 4 let x = function(){
1104 | 5 console.log("Bar");
1105 | 6 };
1106 | 7
1107 | ```
1108 |
1109 |
1110 | - [7.2](#functions--declarations) Use named function expressions instead of function declarations. eslint: [`func-style`](https://eslint.org/docs/rules/func-style)
1111 |
1112 | > Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error's call stack. ([Discussion](https://github.com/airbnb/javascript/issues/794))
1113 |
1114 | ```javascript
1115 | // bad
1116 | function foo(){
1117 | // ...
1118 | }
1119 |
1120 | // okay
1121 | let foo = function(){
1122 | // ...
1123 | };
1124 |
1125 | // good
1126 | // lexical name distinguished from the variable-referenced invocation(s)
1127 | let short = function longUniqueMoreDescriptiveLexicalFoo(){
1128 | // ...
1129 | };
1130 | ```
1131 |
1132 |
1133 | - [7.3](#functions--iife) Wrap immediately invoked function expressions in parentheses. eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife.html)
1134 |
1135 | > Why? An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE.
1136 |
1137 | ```javascript
1138 | // immediately-invoked function expression (IIFE)
1139 |
1140 | // bad
1141 | !function(){ /* ... */ }();
1142 | ~function(){ /* ... */ }();
1143 | -function(){ /* ... */ }();
1144 | +function(){ /* ... */ }();
1145 | void function(){ /* ... */ }();
1146 |
1147 | // good
1148 | (function(){
1149 | console.log("I'm an IIFE");
1150 | }());
1151 | ```
1152 |
1153 |
1154 | - [7.4](#functions--in-blocks) Never declare a function in a non-function block (`if`, `while`, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: [`no-loop-func`](https://eslint.org/docs/rules/no-loop-func.html)
1155 |
1156 |
1157 | - [7.5](#functions--note-on-blocks) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement.
1158 |
1159 | ```javascript
1160 | // bad
1161 | if (currentUser){
1162 | function test(){
1163 | console.log("Foo");
1164 | }
1165 | }
1166 |
1167 | // good
1168 | let test;
1169 | if (currentUser){
1170 | test = () => {
1171 | console.log("Foo");
1172 | };
1173 | }
1174 | ```
1175 |
1176 |
1177 | - [7.6](#functions--arguments-shadow) Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope.
1178 |
1179 | ```javascript
1180 | // bad
1181 | function foo(name, options, arguments){
1182 | // ...
1183 | }
1184 |
1185 | // good
1186 | function foo(name, options, args){
1187 | // ...
1188 | }
1189 | ```
1190 |
1191 |
1192 | - [7.7](#es6-rest) Never use `arguments`, opt to use rest syntax `...` instead. eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params)
1193 |
1194 | > Why? `...` is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like `arguments`.
1195 |
1196 | ```javascript
1197 | // bad
1198 | function concatenateAll(){
1199 | const args = Array.prototype.slice.call(arguments);
1200 | return args.join("");
1201 | }
1202 |
1203 | // good
1204 | function concatenateAll(...args){
1205 | return args.join("");
1206 | }
1207 | ```
1208 |
1209 |
1210 | - [7.8](#es6-default-parameters) Use default parameter syntax rather than mutating function arguments.
1211 |
1212 | ```javascript
1213 | // really bad
1214 | function handleThings(opts){
1215 | // No! We shouldn't mutate function arguments.
1216 | // Double bad: if opts is falsy it'll be set to an object which may
1217 | // be what you want but it can introduce subtle bugs.
1218 | opts = opts || {};
1219 | // ...
1220 | }
1221 |
1222 | // still bad
1223 | function handleThings(opts){
1224 | if (opts === void 0){
1225 | opts = {};
1226 | }
1227 | // ...
1228 | }
1229 |
1230 | // good
1231 | function handleThings(opts = {}){
1232 | // ...
1233 | }
1234 | ```
1235 |
1236 |
1237 | - [7.9](#functions--default-side-effects) Avoid side effects with default parameters.
1238 |
1239 | > Why? They are confusing to reason about.
1240 |
1241 | ```javascript
1242 | let b = 1;
1243 | // bad
1244 | function count(a = b++){
1245 | console.log(a);
1246 | }
1247 | count(); // 1
1248 | count(); // 2
1249 | count(3); // 3
1250 | count(); // 3
1251 | ```
1252 |
1253 |
1254 | - [7.10](#functions--defaults-last) Always put default parameters last.
1255 |
1256 | ```javascript
1257 | // bad
1258 | function handleThings(opts = {}, name){
1259 | // ...
1260 | }
1261 |
1262 | // good
1263 | function handleThings(name, opts = {}){
1264 | // ...
1265 | }
1266 | ```
1267 |
1268 |
1269 | - [7.11](#functions--constructor) Never use the Function constructor to create a new function. eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func)
1270 |
1271 | > Why? Creating a function in this way evaluates a string similarly to `eval()`, which opens vulnerabilities.
1272 |
1273 | ```javascript
1274 | // bad
1275 | let add = new Function("a", "b", "return a + b");
1276 |
1277 | // still bad
1278 | let subtract = Function("a", "b", "return a - b");
1279 | ```
1280 |
1281 |
1282 | - [7.12](#functions--signature-spacing) Spacing in a function signature. eslint: [`space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks)
1283 |
1284 | > Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name.
1285 |
1286 | ```javascript
1287 | // bad
1288 | const f = function () {};
1289 | const g = function (){};
1290 | const h = function() {};
1291 |
1292 | // good
1293 | const x = function(){};
1294 | const y = function a(){};
1295 | ```
1296 |
1297 |
1298 | - [7.13](#functions--mutate-params) Never mutate parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
1299 |
1300 | > Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.
1301 |
1302 | ```javascript
1303 | // bad
1304 | function f1(obj){
1305 | obj.key = 1;
1306 | }
1307 |
1308 | // good
1309 | function f2(obj){
1310 | const key = Object.prototype.hasOwnProperty.call(obj, "key") ? obj.key : 1;
1311 | }
1312 | ```
1313 |
1314 |
1315 | - [7.14](#functions--reassign-params) Never reassign parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
1316 |
1317 | > Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the `arguments` object. It can also cause optimization issues, especially in V8 (and therefore NodeJS as well).
1318 |
1319 | ```javascript
1320 | // bad
1321 | function f1(a){
1322 | a = 1;
1323 | // ...
1324 | }
1325 |
1326 | function f2(a){
1327 | if (!a) a = 1;
1328 | // ...
1329 | }
1330 |
1331 | // good
1332 | function f3(a){
1333 | const b = a || 1;
1334 | // ...
1335 | }
1336 |
1337 | function f4(a = 1){
1338 | // ...
1339 | }
1340 | ```
1341 |
1342 |
1343 | - [7.15](#functions--spread-vs-apply) Prefer the use of the spread operator `...` to call variadic functions. eslint: [`prefer-spread`](https://eslint.org/docs/rules/prefer-spread)
1344 |
1345 | > Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose `new` with `apply`.
1346 |
1347 | ```javascript
1348 | // bad
1349 | const x = [1, 2, 3, 4, 5];
1350 | console.log.apply(console, x);
1351 |
1352 | // good
1353 | const x = [1, 2, 3, 4, 5];
1354 | console.log(...x);
1355 |
1356 | // bad
1357 | new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
1358 |
1359 | // good
1360 | new Date(...[2016, 8, 5]);
1361 | ```
1362 |
1363 |
1364 | - [7.16](#functions--signature-invocation-indentation) Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself and with a trailing comma on the last item. eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline)
1365 |
1366 | ```javascript
1367 | // bad
1368 | function foo(bar,
1369 | baz,
1370 | quux){
1371 | // ...
1372 | }
1373 |
1374 | // good
1375 | function foo(
1376 | bar,
1377 | baz,
1378 | quux,
1379 | ...
1380 | ){
1381 | // ...
1382 | }
1383 |
1384 | // bad
1385 | console.log(foo,
1386 | bar,
1387 | baz);
1388 |
1389 | // good
1390 | console.log(
1391 | foo,
1392 | bar,
1393 | baz,
1394 | ...
1395 | );
1396 | ```
1397 |
1398 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
1399 |
1400 | ## Arrow Functions
1401 |
1402 |
1403 | - [8.1](#arrows--use-them) When you must use an anonymous function (as when passing an inline callback), use arrow function notation. eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing.html)
1404 |
1405 | > Why? It creates a version of the function that executes in the context of `this`, which is usually what you want, and is a more concise syntax.
1406 |
1407 | > Why not? If you have a fairly complicated function, you might move that logic out into its own named function expression.
1408 |
1409 | ```javascript
1410 | // bad
1411 | [1, 2, 3].map(function (x){
1412 | const y = x + 1;
1413 | return x * y;
1414 | });
1415 |
1416 | // good
1417 | [1, 2, 3].map((x) => {
1418 | const y = x + 1;
1419 | return x * y;
1420 | });
1421 | ```
1422 |
1423 |
1424 | - [8.2](#arrows--implicit-return) If the function body consists of a single statement returning an [expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions) without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a `return` statement. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](https://eslint.org/docs/rules/arrow-body-style.html)
1425 |
1426 | > Why? Syntactic sugar. It reads well when multiple functions are chained together.
1427 |
1428 | ```javascript
1429 | // bad
1430 | [1, 2, 3].map(number => {
1431 | const nextNumber = number + 1;
1432 | `A string containing the ${nextNumber}.`;
1433 | });
1434 |
1435 | // good
1436 | [1, 2, 3].map(number => `A string containing the ${number + 1}.`);
1437 |
1438 | // good
1439 | [1, 2, 3].map((number) => {
1440 | const nextNumber = number + 1;
1441 | return `A string containing the ${nextNumber}.`;
1442 | });
1443 |
1444 | // good
1445 | [1, 2, 3].map((number, index) => ({
1446 | [index]: number,
1447 | }));
1448 |
1449 | // No implicit return with side effects
1450 | function foo(callback){
1451 | const val = callback();
1452 | if (val === true){
1453 | // Do something if callback returns true
1454 | }
1455 | }
1456 |
1457 | let bool = false;
1458 |
1459 | // bad
1460 | foo(() => bool = true);
1461 |
1462 | // good
1463 | foo(() => {
1464 | bool = true;
1465 | });
1466 | ```
1467 |
1468 |
1469 | - [8.3](#arrows--paren-wrap) In case the expression spans over multiple lines, wrap it in parentheses for better readability.
1470 |
1471 | > Why? It shows clearly where the function starts and ends.
1472 |
1473 | ```javascript
1474 | // bad
1475 | ["get", "post", "put"].map(httpMethod => Object.prototype.hasOwnProperty.call(
1476 | httpMagicObjectWithAVeryLongName,
1477 | httpMethod,
1478 | )
1479 | );
1480 |
1481 | // good
1482 | ["get", "post", "put"].map(httpMethod => (
1483 | Object.prototype.hasOwnProperty.call(
1484 | httpMagicObjectWithAVeryLongName,
1485 | httpMethod,
1486 | )
1487 | ));
1488 | ```
1489 |
1490 |
1491 | - [8.4](#arrows--one-arg-parens) If your function takes a single argument and doesn’t use braces, omit the parentheses. Otherwise, always include parentheses around arguments for clarity and consistency. Note: it is also acceptable to always use parentheses, in which case use the [“always” option](https://eslint.org/docs/rules/arrow-parens#always) for eslint. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html)
1492 |
1493 | > Why? Less visual clutter.
1494 |
1495 | ```javascript
1496 | // bad
1497 | [1, 2, 3].map((x) => x * x);
1498 |
1499 | // good
1500 | [1, 2, 3].map(x => x * x);
1501 |
1502 | // good
1503 | [1, 2, 3].map(number => (
1504 | `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
1505 | ));
1506 |
1507 | // bad
1508 | [1, 2, 3].map(x => {
1509 | const y = x + 1;
1510 | return x * y;
1511 | });
1512 |
1513 | // good
1514 | [1, 2, 3].map((x) => {
1515 | const y = x + 1;
1516 | return x * y;
1517 | });
1518 | ```
1519 |
1520 |
1521 | - [8.5](#arrows--confusing) Avoid confusing arrow function syntax (`=>`) with comparison operators (`<=`, `>=`). eslint: [`no-confusing-arrow`](https://eslint.org/docs/rules/no-confusing-arrow)
1522 |
1523 | ```javascript
1524 | // bad
1525 | const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
1526 |
1527 | // bad
1528 | const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
1529 |
1530 | // good
1531 | const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
1532 |
1533 | // good
1534 | const itemHeight = (item) => {
1535 | const { height, largeSize, smallSize } = item;
1536 | return height > 256 ? largeSize : smallSize;
1537 | };
1538 | ```
1539 |
1540 |
1541 | - [8.6](#whitespace--implicit-arrow-linebreak) Enforce the location of arrow function bodies with implicit returns. eslint: [`implicit-arrow-linebreak`](https://eslint.org/docs/rules/implicit-arrow-linebreak)
1542 |
1543 | ```javascript
1544 | // bad
1545 | (foo) =>
1546 | bar;
1547 |
1548 | (foo) =>
1549 | (bar);
1550 |
1551 | // good
1552 | (foo) => bar;
1553 | (foo) => (bar);
1554 | (foo) => (
1555 | bar
1556 | )
1557 | ```
1558 |
1559 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
1560 |
1561 | ## Classes & Constructors
1562 |
1563 |
1564 | - [9.1](#constructors--use-class) Always use `class`. Avoid manipulating `prototype` directly.
1565 |
1566 | > Why? `class` syntax is more concise and easier to reason about.
1567 |
1568 | ```javascript
1569 | // bad
1570 | function Queue(contents = []){
1571 | this.queue = [...contents];
1572 | }
1573 |
1574 | Queue.prototype.pop = function(){
1575 | const value = this.queue[0];
1576 | this.queue.splice(0, 1);
1577 | return value;
1578 | };
1579 |
1580 | // good
1581 | class Queue {
1582 | constructor(contents = []){
1583 | this.queue = [...contents];
1584 | }
1585 | pop(){
1586 | const value = this.queue[0];
1587 | this.queue.splice(0, 1);
1588 | return value;
1589 | }
1590 | }
1591 | ```
1592 |
1593 |
1594 | - [9.2](#constructors--extends) Use `extends` for inheritance.
1595 |
1596 | > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`.
1597 |
1598 | ```javascript
1599 | // bad
1600 | const inherits = require("inherits");
1601 | function PeekableQueue(contents){
1602 | Queue.apply(this, contents);
1603 | }
1604 |
1605 | inherits(PeekableQueue, Queue);
1606 | PeekableQueue.prototype.peek = function(){
1607 | return this.queue[0];
1608 | };
1609 |
1610 | // good
1611 | class PeekableQueue extends Queue {
1612 | peek(){
1613 | return this.queue[0];
1614 | }
1615 | }
1616 | ```
1617 |
1618 |
1619 | - [9.3](#constructors--chaining) Methods can return `this` to help with method chaining.
1620 |
1621 | ```javascript
1622 | // bad
1623 | Player.prototype.jump = function(){
1624 | this.jumping = true;
1625 | return true;
1626 | };
1627 |
1628 | Player.prototype.setHeight = function(height){
1629 | this.height = height;
1630 | };
1631 |
1632 | const foo = new Player();
1633 | foo.jump(); // => true
1634 | foo.setHeight(20); // => undefined
1635 |
1636 | // good
1637 | class Player {
1638 | jump(){
1639 | this.jumping = true;
1640 | return this;
1641 | }
1642 |
1643 | setHeight(height){
1644 | this.height = height;
1645 | return this;
1646 | }
1647 | }
1648 |
1649 | const foo = new Player();
1650 |
1651 | foo.jump().setHeight(20);
1652 | ```
1653 |
1654 |
1655 | - [9.4](#constructors--tostring) It’s okay to write a custom `toString()` method, just make sure it works successfully and causes no side effects.
1656 |
1657 | ```javascript
1658 | class Player {
1659 | constructor(options = {}) {
1660 | this.name = options.name || "no name";
1661 | }
1662 |
1663 | getName(){
1664 | return this.name;
1665 | }
1666 |
1667 | toString(){
1668 | return `Player - ${this.getName()}`;
1669 | }
1670 | }
1671 | ```
1672 |
1673 |
1674 | - [9.5](#constructors--no-useless) Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint: [`no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor)
1675 |
1676 | ```javascript
1677 | // bad
1678 | class Player {
1679 | constructor() {}
1680 |
1681 | getName(){
1682 | return this.name;
1683 | }
1684 | }
1685 |
1686 | // bad
1687 | class Foo extends Player {
1688 | constructor(...args){
1689 | super(...args);
1690 | }
1691 | }
1692 |
1693 | // good
1694 | class Foo extends Player {
1695 | constructor(...args){
1696 | super(...args);
1697 | this.name = "Foo";
1698 | }
1699 | }
1700 | ```
1701 |
1702 |
1703 | - [9.6](#classes--no-duplicate-members) Avoid duplicate class members. eslint: [`no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members)
1704 |
1705 | > Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug.
1706 |
1707 | ```javascript
1708 | // bad
1709 | class Foo {
1710 | bar(){ return 1; }
1711 | bar(){ return 2; }
1712 | }
1713 |
1714 | // good
1715 | class Foo {
1716 | bar(){ return 1; }
1717 | }
1718 |
1719 | // good
1720 | class Foo {
1721 | bar(){ return 2; }
1722 | }
1723 | ```
1724 |
1725 |
1726 | - [9.7](#classes--methods-use-this) Class methods should use `this` or be made into a static method unless an external library or framework requires to use specific non-static methods. Being an instance method should indicate that it behaves differently based on properties of the receiver. eslint: [`class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this)
1727 |
1728 | ```javascript
1729 | // bad
1730 | class Foo {
1731 | bar(){
1732 | console.log("bar");
1733 | }
1734 | }
1735 |
1736 | // good - this is used
1737 | class Foo {
1738 | bar(){
1739 | console.log(this.bar);
1740 | }
1741 | }
1742 |
1743 | // good - constructor is exempt
1744 | class Foo {
1745 | constructor(){
1746 | // ...
1747 | }
1748 | }
1749 |
1750 | // good - static methods aren't expected to use this
1751 | class Foo {
1752 | static bar(){
1753 | console.log("bar");
1754 | }
1755 | }
1756 | ```
1757 |
1758 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
1759 |
1760 | ## Modules
1761 |
1762 |
1763 | - [10.1](#modules--use-them) Stick to CommonJS Imports (RequireJS / module.exports).
1764 |
1765 | > Why? Not many of the current browser engines implements import/export from the ES6 standard. In NodeJS, `require()` is still the standard way of importing modules. Plus, in NodeJS you can make use of dynamic module loading and [Require's API's](https://requirejs.org/docs/api.html#config) in general, where you can control caching as well.
1766 |
1767 | ```javascript
1768 | // bad
1769 | import foo from "bar";
1770 | export default bar;
1771 |
1772 | // bad
1773 | import { foo } from "bar";
1774 | export default bar;
1775 |
1776 | // good
1777 | let foo = require("bar");
1778 | module.exports = bar;
1779 | ```
1780 |
1781 |
1782 | - [10.2](#modules--no-duplicate-imports) Only import from a path in one place.
1783 | eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports)
1784 | > Why? Having multiple lines that import from the same path can make code harder to maintain.
1785 |
1786 | ```javascript
1787 | // bad
1788 | let foo = require("bar");
1789 | // … some other imports …
1790 | let { foo2, foo3 } = require("bar");
1791 |
1792 | // good
1793 | let { foo, foo1, foo2 } = require("bar");
1794 |
1795 | // good
1796 | let {
1797 | foo,
1798 | foo1,
1799 | foo2,
1800 | } = require("bar");
1801 | ```
1802 |
1803 |
1804 | - [10.3](#modules--no-mutable-exports) Do not export mutable bindings.
1805 | eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md)
1806 | > Why? Mutation should be avoided in general, but in particular when exporting mutable bindings. While this technique may be needed for some special cases, in general, only constant references should be exported.
1807 |
1808 | ```javascript
1809 | // bad
1810 | let foo = 3;
1811 | module.exports = foo;
1812 |
1813 | // good
1814 | const foo = 3;
1815 | module.exports = foo;
1816 | ```
1817 |
1818 |
1819 | - [10.4](#modules--imports-first) Put all `import`s above non-import statements.
1820 | eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md)
1821 | > Why? Since `import`s are hoisted, keeping them all at the top prevents surprising behavior.
1822 |
1823 | ```javascript
1824 | // bad
1825 | let foo = require("foo");
1826 | foo.init();
1827 |
1828 | let bar = require("bar");
1829 |
1830 | // good
1831 | let foo = require("foo");
1832 | let bar = require("bar");
1833 |
1834 | foo.init();
1835 | ```
1836 |
1837 |
1838 | - [10.5](#modules--import-extensions) Do not include JavaScript filename extensions
1839 | eslint: [`import/extensions`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md)
1840 | > Why? Including extensions inhibits refactoring, and inappropriately hardcodes implementation details of the module you're importing in every consumer.
1841 |
1842 | ```javascript
1843 | // bad
1844 | import foo from "./foo.js";
1845 | import bar from "./bar.jsx";
1846 | import baz from "./baz/index.jsx";
1847 |
1848 | // good
1849 | import foo from "./foo";
1850 | import bar from "./bar";
1851 | import baz from "./baz";
1852 | ```
1853 |
1854 |
1855 | - [10.6](#modules--multiline-imports-over-newlines) Multiline imports should be indented just like multiline array and object literals. eslint: [`object-curly-newline`](https://eslint.org/docs/rules/object-curly-newline)
1856 |
1857 | > Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide.
1858 |
1859 | ```javascript
1860 | // bad
1861 | let { longNameA, longNameB, longNameC, longNameD, longNameE } = require("path");
1862 |
1863 | // good
1864 | let {
1865 | longNameA,
1866 | longNameB,
1867 | longNameC,
1868 | longNameD,
1869 | longNameE,
1870 | } = require("path");
1871 | ```
1872 |
1873 | ## Iterators and Generators
1874 |
1875 |
1876 | - [11.1](#iterators--nope) Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like `for-in` or `for-of`. eslint: [`no-iterator`](https://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](https://eslint.org/docs/rules/no-restricted-syntax)
1877 |
1878 | > Why? Dealing with pure functions that return values is easier to reason about than side effects.
1879 |
1880 | > Use `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... to iterate over arrays, and `Object.keys()` / `Object.values()` / `Object.entries()` to produce arrays so you can iterate over objects.
1881 |
1882 | **Note:** `for-in` and `for-of` are mostly OK as well. Especially for small operations. Higher-order functions however are best practice.
1883 |
1884 | ```javascript
1885 | const numbers = [1, 2, 3, 4, 5];
1886 |
1887 | // ok...
1888 | let sum = 0;
1889 | for (let num of numbers){
1890 | sum += num;
1891 | }
1892 | sum === 15;
1893 |
1894 | // good
1895 | let sum = 0;
1896 | numbers.forEach((num) => {
1897 | sum += num;
1898 | });
1899 | sum === 15;
1900 |
1901 | // best (use the functional force)
1902 | const sum = numbers.reduce((total, num) => total + num, 0);
1903 | sum === 15;
1904 |
1905 | // bad
1906 | const increasedByOne = [];
1907 | for (let i = 0; i < numbers.length; i++){
1908 | increasedByOne.push(numbers[i] + 1);
1909 | }
1910 |
1911 | // good
1912 | const increasedByOne = [];
1913 | numbers.forEach((num) => {
1914 | increasedByOne.push(num + 1);
1915 | });
1916 |
1917 | // best (keeping it functional)
1918 | const increasedByOne = numbers.map(num => num + 1);
1919 | ```
1920 |
1921 |
1922 | - [11.2](#generators--nope) Use generators only in NodeJS for now.
1923 |
1924 | > Why? They don’t transpile well to ES5.
1925 |
1926 |
1927 | - [11.3](#generators--spacing) If you must use generators, or if you disagree with [11.2](#generators--nope), make sure their function signature is spaced properly. eslint: [`generator-star-spacing`](https://eslint.org/docs/rules/generator-star-spacing)
1928 |
1929 | > Why? `function` and `*` are part of the same conceptual keyword - `*` is not a modifier for `function`, `function*` is a unique construct, different from `function`.
1930 |
1931 | ```javascript
1932 | // bad
1933 | function * foo(){
1934 | // ...
1935 | }
1936 |
1937 | // bad
1938 | let bar = function * (){
1939 | // ...
1940 | };
1941 |
1942 | // bad
1943 | let baz = function *(){
1944 | // ...
1945 | };
1946 |
1947 | // bad
1948 | let quux = function* (){
1949 | // ...
1950 | };
1951 |
1952 | // bad
1953 | function*foo(){
1954 | // ...
1955 | }
1956 |
1957 | // bad
1958 | function *foo(){
1959 | // ...
1960 | }
1961 |
1962 | // very bad
1963 | function
1964 | *
1965 | foo(){
1966 | // ...
1967 | }
1968 |
1969 | // very bad
1970 | let foo = function
1971 | *
1972 | (){
1973 | // ...
1974 | };
1975 |
1976 | // good
1977 | function* foo(){
1978 | // ...
1979 | }
1980 |
1981 | // good
1982 | let foo = function*(){
1983 | // ...
1984 | };
1985 | ```
1986 |
1987 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
1988 |
1989 | ## Properties
1990 |
1991 |
1992 | - [12.1](#properties--dot) Use dot notation when accessing properties. eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation.html)
1993 |
1994 | ```javascript
1995 | const foo = {
1996 | bar: true,
1997 | baz: 5,
1998 | };
1999 |
2000 | // bad
2001 | const isTrue = foo["bar"];
2002 |
2003 | // good
2004 | const isTrue = foo.bar;
2005 | ```
2006 |
2007 |
2008 | - [12.2](#properties--bracket) Use bracket notation `[]` when accessing properties with a variable or if the key includes illegal characters.
2009 |
2010 | ```javascript
2011 | const foo = {
2012 | bar: true,
2013 | baz: 5,
2014 | "test-1": "foo"
2015 | };
2016 |
2017 | function getProp(prop) {
2018 | return foo[prop];
2019 | }
2020 |
2021 | const isTrue = getProp("bar");
2022 | const bar = foo["test-1"];
2023 | ```
2024 |
2025 | - [12.3](#es2016-properties--exponentiation-operator) Use exponentiation operator `**` when calculating exponentiations. eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties).
2026 |
2027 | ```javascript
2028 | // bad
2029 | const binary = Math.pow(2, 10);
2030 |
2031 | // good
2032 | const binary = 2 ** 10;
2033 | ```
2034 |
2035 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2036 |
2037 | ## Variables
2038 |
2039 |
2040 | - [13.1](#variables--const) Always use `const` or `let` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Variables declared without `const`, `let` or `var` are disallowed in [strict mode](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Strict_mode) as well. eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const)
2041 |
2042 | ```javascript
2043 | // bad
2044 | foo = new Foo();
2045 |
2046 | // good
2047 | const foo = new Foo();
2048 | ```
2049 |
2050 |
2051 | - [13.2](#variables--one-const) Use one `const` or `let` declaration per variable or assignment. eslint: [`one-var`](https://eslint.org/docs/rules/one-var.html)
2052 |
2053 | > Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.
2054 |
2055 | ```javascript
2056 | // bad
2057 | const items = getItems(),
2058 | foo = true,
2059 | bar = "baz";
2060 |
2061 | // bad
2062 | // (compare to above, and try to spot the mistake)
2063 | const items = getItems(),
2064 | foo = true;
2065 | bar = "baz";
2066 |
2067 | // good
2068 | const items = getItems();
2069 | const foo = true;
2070 | const bar = "baz";
2071 | ```
2072 |
2073 |
2074 | - [13.3](#variables--const-let-group) Group all your `const`s and then group all your `let`s.
2075 |
2076 | > Why? This is helpful when later on you might need to assign a variable depending on one of the previously assigned variables.
2077 |
2078 | ```javascript
2079 | // bad
2080 | let i, len, foo,
2081 | items = getItems(),
2082 | bar = true;
2083 |
2084 | // bad
2085 | let i;
2086 | const items = getItems();
2087 | let foo;
2088 | const bar = true;
2089 | let len;
2090 |
2091 | // good
2092 | const bar = true;
2093 | const items = getItems();
2094 | let foo;
2095 | let i;
2096 | let length;
2097 | ```
2098 |
2099 |
2100 | - [13.4](#variables--define-where-used) Assign variables where you need them, but place them in a reasonable place.
2101 |
2102 | > Why? `let` and `const` are block scoped and not function scoped.
2103 |
2104 | ```javascript
2105 | // bad - unnecessary function call
2106 | function checkName(hasName){
2107 | const name = getName();
2108 |
2109 | if (hasName === "test") return false;
2110 |
2111 | if (name === "test"){
2112 | this.setName("");
2113 | return false;
2114 | }
2115 |
2116 | return name;
2117 | }
2118 |
2119 | // good
2120 | function checkName(hasName){
2121 | if (hasName === "test") return false;
2122 |
2123 | const name = getName();
2124 |
2125 | if (name === "test"){
2126 | this.setName("");
2127 | return false;
2128 | }
2129 |
2130 | return name;
2131 | }
2132 | ```
2133 |
2134 | - [13.5](#variables--no-chain-assignment) Don’t chain variable assignments. eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign)
2135 |
2136 | > Why? Chaining variable assignments creates implicit global variables.
2137 |
2138 | ```javascript
2139 | // bad
2140 | (function example(){
2141 | /**
2142 | * JavaScript interprets this as
2143 | * let a = ( b = ( c = 1 ) );
2144 | * The let keyword only applies to variable a; variables b and c become
2145 | * global variables.
2146 | */
2147 | let a = b = c = 1;
2148 | }());
2149 |
2150 | console.log(a); // throws ReferenceError
2151 | console.log(b); // 1
2152 | console.log(c); // 1
2153 |
2154 | // good
2155 | (function example(){
2156 | let a = 1;
2157 | let b = a;
2158 | let c = a;
2159 | }());
2160 |
2161 | console.log(a); // throws ReferenceError
2162 | console.log(b); // throws ReferenceError
2163 | console.log(c); // throws ReferenceError
2164 |
2165 | // the same applies for `const`
2166 | ```
2167 |
2168 |
2169 | - [13.6](#variables--unary-increment-decrement) Avoid using unary increments and decrements (`++`, `--`). eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus)
2170 |
2171 | > Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like `num += 1` instead of `num++` or `num ++`. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.
2172 |
2173 | ```javascript
2174 | // bad
2175 |
2176 | const array = [1, 2, 3];
2177 | let num = 1;
2178 | num++;
2179 | --num;
2180 |
2181 | let sum = 0;
2182 | let truthyCount = 0;
2183 | for (let i = 0; i < array.length; i++){
2184 | let value = array[i];
2185 | sum += value;
2186 | if (value) truthyCount++;
2187 | }
2188 |
2189 | // good
2190 |
2191 | const array = [1, 2, 3];
2192 | let num = 1;
2193 | num += 1;
2194 | num -= 1;
2195 |
2196 | const sum = array.reduce((a, b) => a + b, 0);
2197 | const truthyCount = array.filter(Boolean).length;
2198 | ```
2199 |
2200 |
2201 | - [13.7](#variables--linebreak) Avoid linebreaks before or after `=` in an assignment. If your assignment violates [`max-len`](https://eslint.org/docs/rules/max-len.html), surround the value in parens. eslint [`operator-linebreak`](https://eslint.org/docs/rules/operator-linebreak.html).
2202 |
2203 | > Why? Linebreaks surrounding `=` can obfuscate the value of an assignment.
2204 |
2205 | ```javascript
2206 | // bad
2207 | const foo =
2208 | superLongLongLongLongLongLongLongLongFunctionName();
2209 |
2210 | // bad
2211 | const foo
2212 | = "superLongLongLongLongLongLongLongLongString";
2213 |
2214 | // good
2215 | const foo = (
2216 | superLongLongLongLongLongLongLongLongFunctionName()
2217 | );
2218 |
2219 | // good
2220 | const foo = "superLongLongLongLongLongLongLongLongString";
2221 | ```
2222 |
2223 |
2224 | - [13.8](#variables--no-unused-vars) Disallow unused variables. eslint: [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars)
2225 |
2226 | > Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.
2227 |
2228 | ```javascript
2229 | // bad
2230 |
2231 | let some_unused_var = 42;
2232 |
2233 | // Write-only variables are not considered as used.
2234 | let y = 10;
2235 | y = 5;
2236 |
2237 | // A read for a modification of itself is not considered as used.
2238 | let z = 0;
2239 | z = z + 1;
2240 |
2241 | // Unused function arguments.
2242 | function getX(x, y){
2243 | return x;
2244 | }
2245 |
2246 | // good
2247 |
2248 | function getXPlusY(x, y){
2249 | return x + y;
2250 | }
2251 |
2252 | let x = 1;
2253 | let y = a + 2;
2254 |
2255 | alert(getXPlusY(x, y));
2256 |
2257 | // 'type' is ignored even if unused because it has a rest property sibling.
2258 | // This is a form of extracting an object that omits the specified keys.
2259 | let { type, ...coords } = data;
2260 | // 'coords' is now the 'data' object without its 'type' property.
2261 | ```
2262 |
2263 |
2264 | - [13.9](#variables--bitwise-floor) Do not floor variables with the bitwise or (`x | 0`). Use `Math.floor()` or if you must `Math.trunc()`.
2265 | > Why? First off, it does **not** _floor_ the number. It _truncates_ it (rounding towards 0). It causes odd Comparative behavior as well: `Math.floor(NaN) === NaN`, while `(NaN | 0) === 0`. Also, it works with 32-bit signed integers only. As mentioned above, use `Math.trunc()` if you have to. It is the ES5 equivalent of `| 0` and it is able to work with numbers higher or equal to 2^31.
2266 |
2267 | ```javascript
2268 | let x = 42.835
2269 |
2270 | // Bad
2271 | let y = x | 0;
2272 |
2273 | // Okay
2274 | let y = Math.trunc(x);
2275 |
2276 | // Good
2277 | let y = Math.floor(x);
2278 | ```
2279 |
2280 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2281 |
2282 | ## Hoisting
2283 |
2284 |
2285 | - [14.1](#hoisting--about) `var` declarations get hoisted to the top of their closest enclosing function scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone). It’s important to know why [typeof is no longer safe](https://web.archive.org/web/20200121061528/http://es-discourse.com/t/why-typeof-is-no-longer-safe/15).
2286 |
2287 | ```javascript
2288 | // this wouldn’t work (assuming there
2289 | // is no notDefined global variable)
2290 | function example(){
2291 | console.log(notDefined); // => throws a ReferenceError
2292 | }
2293 |
2294 | // creating a variable declaration after you
2295 | // reference the variable will work due to
2296 | // variable hoisting. Note: the assignment
2297 | // value of `true` is not hoisted.
2298 | function example(){
2299 | console.log(declaredButNotAssigned); // => undefined
2300 | var declaredButNotAssigned = true;
2301 | }
2302 |
2303 | // the interpreter is hoisting the variable
2304 | // declaration to the top of the scope,
2305 | // which means our example could be rewritten as:
2306 | function example(){
2307 | let declaredButNotAssigned;
2308 | console.log(declaredButNotAssigned); // => undefined
2309 | declaredButNotAssigned = true;
2310 | }
2311 |
2312 | // using const and let
2313 | function example(){
2314 | console.log(declaredButNotAssigned); // => throws a ReferenceError
2315 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
2316 | const declaredButNotAssigned = true;
2317 | }
2318 | ```
2319 |
2320 |
2321 | - [14.2](#hoisting--anon-expressions) Anonymous function expressions hoist their variable name, but not the function assignment.
2322 |
2323 | ```javascript
2324 | function example(){
2325 | console.log(anonymous); // => undefined
2326 |
2327 | anonymous(); // => TypeError anonymous is not a function
2328 |
2329 | var anonymous = function(){
2330 | console.log("anonymous function expression");
2331 | };
2332 | }
2333 | ```
2334 |
2335 |
2336 | - [14.3](#hoisting--named-expressions) Named function expressions hoist the variable name, not the function name or the function body.
2337 |
2338 | ```javascript
2339 | function example(){
2340 | console.log(named); // => undefined
2341 |
2342 | named(); // => TypeError named is not a function
2343 |
2344 | foo(); // => ReferenceError foo is not defined
2345 |
2346 | var named = function foo(){
2347 | console.log("bar");
2348 | };
2349 | }
2350 |
2351 | // the same is true when the function name
2352 | // is the same as the variable name.
2353 | function example(){
2354 | console.log(named); // => undefined
2355 |
2356 | named(); // => TypeError named is not a function
2357 |
2358 | var named = function named(){
2359 | console.log("named");
2360 | };
2361 | }
2362 | ```
2363 |
2364 |
2365 | - [14.4](#hoisting--declarations) Function declarations hoist their name and the function body.
2366 |
2367 | ```javascript
2368 | function example(){
2369 | foo(); // => bar
2370 |
2371 | function foo(){
2372 | console.log("bar");
2373 | }
2374 | }
2375 | ```
2376 |
2377 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/).
2378 |
2379 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2380 |
2381 | ## Comparison Operators & Equality
2382 |
2383 |
2384 | - [15.1](#comparison--eqeqeq) Use `===` and `!==` over `==` and `!=` (Strict comparison). eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq.html)
2385 |
2386 |
2387 | - [15.2](#comparison--if) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules:
2388 |
2389 | - **Objects** evaluate to **true**
2390 | - **Undefined** evaluates to **false**
2391 | - **Null** evaluates to **false**
2392 | - **Booleans** evaluate to **the value of the boolean**
2393 | - **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true**
2394 | - **Strings** evaluate to **false** if an empty string `''`, otherwise **true**
2395 |
2396 | ```javascript
2397 | if ([0] && []){
2398 | // true
2399 | // an array (even an empty one) is an object, objects will evaluate to true
2400 | }
2401 | ```
2402 |
2403 |
2404 | - [15.3](#comparison--shortcuts) Use shortcuts for booleans, but explicit comparisons for strings and numbers.
2405 |
2406 | ```javascript
2407 | // bad
2408 | if (isValid === true){
2409 | // ...
2410 | }
2411 |
2412 | // good
2413 | if (isValid){
2414 | // ...
2415 | }
2416 |
2417 | // bad
2418 | if (name){
2419 | // ...
2420 | }
2421 |
2422 | // good
2423 | if (name !== ""){
2424 | // ...
2425 | }
2426 |
2427 | // bad
2428 | if (collection.length){
2429 | // ...
2430 | }
2431 |
2432 | // good
2433 | if (collection.length > 0){
2434 | // ...
2435 | }
2436 | ```
2437 |
2438 |
2439 | - [15.4](#comparison--moreinfo) For more information see [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll.
2440 |
2441 |
2442 | - [15.5](#comparison--switch-blocks) Use braces to create blocks in `case` and `default` clauses. This is especially useful for those, which contain lexical declarations (e.g. `let`, `const`, `function`, and `class`). eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations.html)
2443 |
2444 | > Why? Lexical declarations are visible in the entire `switch` block but only get initialized when assigned, which only happens when its `case` is reached. This causes problems when multiple `case` clauses attempt to define the same thing.
2445 |
2446 | ```javascript
2447 | // bad
2448 | switch (foo){
2449 | case 1:
2450 | let x = 1;
2451 | break;
2452 | case 2:
2453 | const y = 2;
2454 | break;
2455 | case 3:
2456 | function f(){
2457 | // ...
2458 | }
2459 | break;
2460 | default:
2461 | class C {}
2462 | }
2463 |
2464 | // good
2465 | switch (foo) {
2466 | case 1: {
2467 | let x = 1;
2468 | break;
2469 | }
2470 | case 2: {
2471 | const y = 2;
2472 | break;
2473 | }
2474 | case 3: {
2475 | function f(){
2476 | // ...
2477 | }
2478 | break;
2479 | }
2480 | case 4: {
2481 | bar();
2482 | break;
2483 | }
2484 | default: {
2485 | class C {}
2486 | }
2487 | }
2488 | ```
2489 |
2490 |
2491 | - [15.6](#comparison--nested-ternaries) Ternaries should not be nested and generally be single line expressions. eslint: [`no-nested-ternary`](https://eslint.org/docs/rules/no-nested-ternary.html)
2492 |
2493 | ```javascript
2494 | // bad
2495 | const foo = maybe1 > maybe2
2496 | ? "bar"
2497 | : value1 > value2 ? "baz" : null;
2498 |
2499 | // split into 2 separated ternary expressions
2500 | const maybeNull = value1 > value2 ? "baz" : null;
2501 |
2502 | // better
2503 | const foo = maybe1 > maybe2
2504 | ? "bar"
2505 | : maybeNull;
2506 |
2507 | // best
2508 | const foo = maybe1 > maybe2 ? "bar" : maybeNull;
2509 | ```
2510 |
2511 |
2512 | - [15.7](#comparison--unneeded-ternary) Avoid unneeded ternary statements. eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary.html)
2513 |
2514 | ```javascript
2515 | // bad
2516 | const foo = a ? a : b;
2517 | const bar = c ? true : false;
2518 | const baz = c ? false : true;
2519 |
2520 | // good
2521 | const foo = a || b;
2522 | const bar = !!c;
2523 | const baz = !c;
2524 | ```
2525 |
2526 |
2527 | - [15.8](#comparison--no-mixed-operators) When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators (`+`, `-`, `*`, & `/`) since their precedence is broadly understood. eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators.html)
2528 |
2529 | > Why? This improves readability and clarifies the developer’s intention.
2530 |
2531 | ```javascript
2532 | // bad
2533 | const foo = a && b < 0 || c > 0 || d + 1 === 0;
2534 |
2535 | // bad
2536 | const bar = a ** b - 5 % d;
2537 |
2538 | // bad
2539 | // one may be confused into thinking (a || b) && c
2540 | if (a || b && c){
2541 | return d;
2542 | }
2543 |
2544 | // good
2545 | const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
2546 |
2547 | // good
2548 | const bar = (a ** b) - (5 % d);
2549 |
2550 | // good
2551 | if (a || (b && c)){
2552 | return d;
2553 | }
2554 |
2555 | // good
2556 | const bar = a + b / c * d;
2557 | ```
2558 |
2559 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2560 |
2561 | ## Blocks
2562 |
2563 |
2564 | - [16.1](#blocks--braces) Use braces with all multi-line blocks. eslint: [`nonblock-statement-body-position`](https://eslint.org/docs/rules/nonblock-statement-body-position)
2565 |
2566 | > Note: Use one-line statements only with short commands. Like `if (x) y();`
They look nicer but can be hard to debug
2567 |
2568 | ```javascript
2569 | // bad
2570 | if (test)
2571 | return false;
2572 |
2573 | // good
2574 | if (test) return false;
2575 |
2576 | // bad - it is too bulky and doesn't fit nicely in one row
2577 | if (test1) if (test2) stuff();
2578 |
2579 | // good
2580 | if (test1){
2581 | if (test2) stuff();
2582 | }
2583 |
2584 | // good (multiline)
2585 | if (test){
2586 | doStuff();
2587 | // ...
2588 | return false;
2589 | }
2590 |
2591 | // bad
2592 | function foo(){ return false; }
2593 |
2594 | // good
2595 | function bar(){
2596 | return false;
2597 | }
2598 | ```
2599 |
2600 |
2601 | - [16.2](#blocks--cuddled-elses) If you're using multi-line blocks with `if` and `else`, do not put `else` on the same line as your `if` block’s closing brace. eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style.html)
2602 |
2603 | ```javascript
2604 | // bad
2605 | if (test){
2606 | thing1();
2607 | thing2();
2608 | } else {
2609 | thing3();
2610 | thing4();
2611 | }
2612 |
2613 | // good
2614 | if (test){
2615 | thing1();
2616 | thing2();
2617 | }
2618 | else {
2619 | thing3();
2620 | thing4();
2621 | }
2622 |
2623 | // bad
2624 | if (test){
2625 | thing1();
2626 | thing2();
2627 | } else thing3();
2628 |
2629 | // good
2630 | if (test){
2631 | thing1();
2632 | thing2();
2633 | }
2634 | else thing3();
2635 | ```
2636 |
2637 |
2638 | - [16.3](#blocks--no-else-return) If an `if` block always executes a `return` statement, the subsequent `else` block is unnecessary. A `return` in an `else if` block following an `if` block that contains a `return` can be separated into multiple `if` blocks. eslint: [`no-else-return`](https://eslint.org/docs/rules/no-else-return)
2639 |
2640 | ```javascript
2641 | // bad
2642 | function foo(){
2643 | if (x) return x;
2644 | else return y;
2645 | }
2646 |
2647 | // bad
2648 | function bar(){
2649 | if (x) return x;
2650 | else if (y) return y;
2651 | }
2652 |
2653 | // bad
2654 | function baz(){
2655 | if (x) return x;
2656 | else {
2657 | if (y) return y;
2658 | }
2659 | }
2660 |
2661 | // good
2662 | function foo(){
2663 | if (x) return x;
2664 | return y;
2665 | }
2666 |
2667 | // good
2668 | function bar(){
2669 | if (x) return x;
2670 | if (y) return y;
2671 | }
2672 |
2673 | // good
2674 | function baz(x){
2675 | if (x){
2676 | if (z) return y;
2677 | }
2678 | else return z;
2679 | }
2680 | ```
2681 |
2682 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2683 |
2684 | ## Control Statements
2685 |
2686 |
2687 | - [17.1](#control-statements) In case your control statement (`if`, `while` etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.
2688 |
2689 | > Why? Requiring operators at the beginning of the line keeps the operators aligned and follows a pattern similar to method chaining. This also improves readability by making it easier to visually follow complex logic.
2690 |
2691 | ```javascript
2692 | // bad
2693 | if ((foo === 123 || bar === "abc") && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()){
2694 | thing1();
2695 | }
2696 |
2697 | // bad
2698 | if (foo === 123 &&
2699 | bar === "abc"){
2700 | thing1();
2701 | }
2702 |
2703 | // bad
2704 | if (foo === 123
2705 | && bar === "abc"){
2706 | thing1();
2707 | }
2708 |
2709 | // bad
2710 | if (
2711 | foo === 123 &&
2712 | bar === "abc"
2713 | ){
2714 | thing1();
2715 | }
2716 |
2717 | // good
2718 | if (
2719 | foo === 123
2720 | && bar === "abc"
2721 | ){
2722 | thing1();
2723 | }
2724 |
2725 | // good
2726 | if (
2727 | (foo === 123 || bar === "abc")
2728 | && doesItLookGoodWhenItBecomesThatLong()
2729 | && isThisReallyHappening()
2730 | ){
2731 | thing1();
2732 | }
2733 |
2734 | // good
2735 | if (foo === 123 && bar === "abc"){
2736 | thing1();
2737 | }
2738 | ```
2739 |
2740 |
2741 | - [17.2](#control-statements--value-selection) Don't use selection operators in place of control statements.
2742 |
2743 | ```javascript
2744 | // bad
2745 | !isRunning && startRunning();
2746 |
2747 | // good
2748 | if (!isRunning) startRunning();
2749 | ```
2750 |
2751 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2752 |
2753 | ## Comments
2754 |
2755 |
2756 | - [18.1](#comments--language) Stick to the english language. Always write variable names, function names, comments and co in english.
2757 |
2758 | > Why? Some reasons:
2759 | > - Consistency.
2760 | > - English is a global language. What if you're part of a german developer team, write code in german and then want to hire someone from another country?
2761 | > - JavaScript's keywords are english.
2762 | > - Some languages use symbols from different charsets (ö, ä, ü, ß, Ѱ, Ω, etc. pp.). Some of them are illegal as variable/function names and others could break your encoding.
2763 |
2764 |
2765 | - [18.2](#comments--multiline) Use `/** ... */` for multi-line comments.
2766 |
2767 | ```javascript
2768 | // bad
2769 | // make() returns a new element
2770 | // based on the passed in tag name
2771 | //
2772 | // @param {String} tag
2773 | // @return {Element} element
2774 | function make(tag){
2775 |
2776 | // ...
2777 |
2778 | return element;
2779 | }
2780 |
2781 | // good
2782 | /**
2783 | * make() returns a new element
2784 | * based on the passed-in tag name
2785 | */
2786 | function make(tag){
2787 |
2788 | // ...
2789 |
2790 | return element;
2791 | }
2792 | ```
2793 |
2794 |
2795 | - [18.3](#comments--singleline) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.
2796 |
2797 | ```javascript
2798 | // bad
2799 | const active = true; // is current tab
2800 |
2801 | // good
2802 | // is current tab
2803 | const active = true;
2804 |
2805 | // bad
2806 | function getType(){
2807 | console.log("fetching type...");
2808 | // set the default type to 'no type'
2809 | const type = this.type || "no type";
2810 |
2811 | return type;
2812 | }
2813 |
2814 | // good
2815 | function getType(){
2816 | console.log("fetching type...");
2817 |
2818 | // set the default type to 'no type'
2819 | const type = this.type || "no type";
2820 |
2821 | return type;
2822 | }
2823 |
2824 | // also good
2825 | function getType(){
2826 | // set the default type to 'no type'
2827 | const type = this.type || "no type";
2828 |
2829 | return type;
2830 | }
2831 | ```
2832 |
2833 |
2834 | - [18.4](#comments--spaces) Start all comments with a space to make it easier to read. eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment)
2835 |
2836 | ```javascript
2837 | // bad
2838 | //is current tab
2839 | const active = true;
2840 |
2841 | // good
2842 | // is current tab
2843 | const active = true;
2844 |
2845 | // bad
2846 | /**
2847 | *make() returns a new element
2848 | *based on the passed-in tag name
2849 | */
2850 | function make(tag){
2851 |
2852 | // ...
2853 |
2854 | return element;
2855 | }
2856 |
2857 | // good
2858 | /**
2859 | * make() returns a new element
2860 | * based on the passed-in tag name
2861 | */
2862 | function make(tag){
2863 |
2864 | // ...
2865 |
2866 | return element;
2867 | }
2868 | ```
2869 |
2870 |
2871 | - [18.5](#comments--actionitems) Prefixing your comments with `FIXME` or `TODO` (action-items) helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME: -- need to figure this out` or `TODO: -- need to implement`.
2872 |
2873 |
2874 | - [18.6](#comments--fixme) Use `// FIXME:` to annotate problems.
2875 |
2876 | ```javascript
2877 | class Calculator extends Abacus {
2878 | constructor(){
2879 | super();
2880 |
2881 | // FIXME: shouldn’t use a global here
2882 | total = 0;
2883 | }
2884 | }
2885 | ```
2886 |
2887 |
2888 | - [18.7](#comments--todo) Use `// TODO:` to annotate solutions to problems.
2889 |
2890 | ```javascript
2891 | class Calculator extends Abacus {
2892 | constructor(){
2893 | super();
2894 |
2895 | // TODO: total should be configurable by an options param
2896 | this.total = 0;
2897 | }
2898 | }
2899 | ```
2900 |
2901 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
2902 |
2903 | ## Whitespace
2904 |
2905 |
2906 | - [19.1](#whitespace--spaces) Use soft tabs (space character instead of tabulator) set to **4** spaces. eslint: [`indent`](https://eslint.org/docs/rules/indent.html)
2907 |
2908 | ```javascript
2909 | // bad
2910 | function foo(){
2911 | ∙∙let name;
2912 | let foo;
2913 | }
2914 |
2915 | // bad
2916 | function bar(){
2917 | ∙let name;
2918 | let foo;
2919 | }
2920 |
2921 | // good
2922 | function baz(){
2923 | ∙∙∙∙let name;
2924 | let foo;
2925 | }
2926 | ```
2927 |
2928 |
2929 | - [19.2](#whitespace--before-blocks) Place 1 space before the leading brace **if** the character before it is _not_ a parenthesis. eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks.html)
2930 |
2931 | > Why? Technically speaking, the block is part of the expression. A space doesn't really make sense unless it is simpler to read.
2932 |
2933 | ```javascript
2934 | // bad
2935 | function test() {
2936 | console.log("test");
2937 | }
2938 |
2939 | // good
2940 | function test(){
2941 | console.log("test");
2942 | }
2943 |
2944 | // bad
2945 | foo.set("attr",{
2946 | bar: "baz"
2947 | });
2948 |
2949 | // good
2950 | foo.set("attr", {
2951 | bar: "baz"
2952 | });
2953 |
2954 | // bad
2955 | class Foo{
2956 | // ...
2957 | }
2958 |
2959 | // good
2960 | class Foo {
2961 | // ...
2962 | }
2963 |
2964 | // bad
2965 | constructor() {
2966 | // ...
2967 | }
2968 |
2969 | // good
2970 | constructor(){
2971 | // ...
2972 | }
2973 |
2974 | // bad
2975 | if (foo) {
2976 | bar();
2977 | }
2978 | else{
2979 | baz();
2980 | }
2981 |
2982 | // good
2983 | if (foo){
2984 | bar();
2985 | }
2986 | else {
2987 | baz();
2988 | }
2989 |
2990 | // bad
2991 | let x ={
2992 | foo: "bar",
2993 | };
2994 |
2995 | // good
2996 | let x = {
2997 | foo: "bar",
2998 | };
2999 | ```
3000 |
3001 |
3002 | - [19.3](#whitespace--around-keywords) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space between the argument list and the function name in function calls and declarations. eslint: [`keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing.html)
3003 |
3004 | ```javascript
3005 | // bad
3006 | if(foo){
3007 | bar ();
3008 | }
3009 |
3010 | // good
3011 | if (foo){
3012 | bar();
3013 | }
3014 |
3015 | // bad
3016 | function foo (){
3017 | console.log ("bar");
3018 | }
3019 |
3020 | // good
3021 | function foo(){
3022 | console.log("bar");
3023 | }
3024 | ```
3025 |
3026 |
3027 | - [19.4](#whitespace--infix-ops) Set off operators with spaces. eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops.html)
3028 |
3029 | ```javascript
3030 | // bad
3031 | const x=y+5;
3032 |
3033 | // good
3034 | const x = y + 5;
3035 | ```
3036 |
3037 |
3038 | - [19.5](#whitespace--lf-linebreaks) Use Unix/Linux-Style Linebreaks - `LF` (`\n`) instead of `CR` + `LF` (`\r\n`). eslint: [`linebreak-style`](https://eslint.org/docs/rules/linebreak-style)
3039 |
3040 |
3041 | - [19.6](#whitespace--newline-at-end) End files with a single newline character. eslint: [`eol-last`](https://eslint.org/docs/rules/eol-last)
3042 |
3043 | > Why? Because that's how the POSIX standard defines a line. [Read more...](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206)
3044 |
3045 | ```javascript
3046 | // bad
3047 | let foo = require("foo");
3048 | // ...
3049 | module.exports = foo;
3050 | ```
3051 |
3052 | ```javascript
3053 | // bad
3054 | let foo = require("foo");
3055 | // ...
3056 | module.exports = foo;↵
3057 | ↵
3058 | ```
3059 |
3060 | ```javascript
3061 | // good
3062 | let foo = require("foo");
3063 | // ...
3064 | module.exports = foo;↵
3065 | ```
3066 |
3067 |
3068 | - [19.7](#whitespace--chains) Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which
3069 | emphasizes that the line is a method call, not a new statement. eslint: [`newline-per-chained-call`](https://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](https://eslint.org/docs/rules/no-whitespace-before-property)
3070 |
3071 | ```javascript
3072 | // bad
3073 | $("#items").find(".selected").highlight().end().find(".open").updateCount();
3074 |
3075 | // bad
3076 | $("#items").
3077 | find(".selected").
3078 | highlight().
3079 | end().
3080 | find(".open").
3081 | updateCount();
3082 |
3083 | // good
3084 | $("#items")
3085 | .find(".selected")
3086 | .highlight()
3087 | .end()
3088 | .find(".open")
3089 | .updateCount();
3090 |
3091 | // bad
3092 | const leds = stage.selectAll(".led").data(data).enter().append("svg:svg").classed("led", true)
3093 | .attr("width", (radius + margin) * 2).append("svg:g")
3094 | .attr("transform", `translate(${radius + margin}, ${radius + margin})`)
3095 | .call(tron.led);
3096 |
3097 | // good
3098 | const leds = stage.selectAll(".led")
3099 | .data(data)
3100 | .enter().append("svg:svg")
3101 | .classed("led", true)
3102 | .attr("width", (radius + margin) * 2)
3103 | .append("svg:g")
3104 | .attr("transform", `translate(${radius + margin}, ${radius + margin})`)
3105 | .call(tron.led);
3106 |
3107 | // good
3108 | const leds = stage.selectAll(".led").data(data);
3109 | ```
3110 |
3111 |
3112 | - [19.8](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement.
3113 |
3114 | ```javascript
3115 | // bad
3116 | if (foo){
3117 | return bar;
3118 | }
3119 | return baz;
3120 |
3121 | // good
3122 | if (foo){
3123 | return bar;
3124 | }
3125 |
3126 | return baz;
3127 |
3128 | // bad
3129 | const obj = {
3130 | foo(){
3131 | },
3132 | bar(){
3133 | }
3134 | };
3135 | return obj;
3136 |
3137 | // good
3138 | const obj = {
3139 | foo(){
3140 | },
3141 |
3142 | bar(){
3143 | }
3144 | };
3145 |
3146 | return obj;
3147 |
3148 | // bad
3149 | const arr = [
3150 | function foo(){
3151 | },
3152 | function bar(){
3153 | }
3154 | ];
3155 | return arr;
3156 |
3157 | // good
3158 | const arr = [
3159 | function foo(){
3160 | },
3161 |
3162 | function bar(){
3163 | }
3164 | ];
3165 |
3166 | return arr;
3167 | ```
3168 |
3169 |
3170 | - [19.9](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](https://eslint.org/docs/rules/padded-blocks.html)
3171 |
3172 | ```javascript
3173 | // bad
3174 | function bar(){
3175 |
3176 | console.log(foo);
3177 |
3178 | }
3179 |
3180 | // bad
3181 | if (baz){
3182 |
3183 | console.log(qux);
3184 | }
3185 | else {
3186 | console.log(foo);
3187 |
3188 | }
3189 |
3190 | // bad
3191 | class Foo {
3192 |
3193 | constructor(bar){
3194 | this.bar = bar;
3195 | }
3196 | }
3197 |
3198 | // good
3199 | function bar(){
3200 | console.log(foo);
3201 | }
3202 |
3203 | // good
3204 | if (baz){
3205 | console.log(qux);
3206 | }
3207 | else {
3208 | console.log(foo);
3209 | }
3210 | ```
3211 |
3212 |
3213 | - [19.10](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](https://eslint.org/docs/rules/space-in-parens.html)
3214 |
3215 | ```javascript
3216 | // bad
3217 | function bar( foo ){
3218 | return foo;
3219 | }
3220 |
3221 | // good
3222 | function bar(foo){
3223 | return foo;
3224 | }
3225 |
3226 | // bad
3227 | if ( foo ){
3228 | console.log(foo);
3229 | }
3230 |
3231 | // good
3232 | if (foo){
3233 | console.log(foo);
3234 | }
3235 | ```
3236 |
3237 |
3238 | - [19.11](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](https://eslint.org/docs/rules/array-bracket-spacing.html)
3239 |
3240 | ```javascript
3241 | // bad
3242 | const foo = [ 1, 2, 3 ];
3243 | console.log(foo[ 0 ]);
3244 |
3245 | // good
3246 | const foo = [1, 2, 3];
3247 | console.log(foo[0]);
3248 | ```
3249 |
3250 |
3251 | - [19.12](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing.html)
3252 |
3253 | ```javascript
3254 | // bad
3255 | const foo = {foo: "bar"};
3256 |
3257 | // good
3258 | const foo = { foo: "bar" };
3259 | ```
3260 |
3261 |
3262 | - [19.13](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](https://eslint.org/docs/rules/max-len.html)
3263 |
3264 | > Why? This ensures readability and maintainability.
3265 |
3266 | ```javascript
3267 | // bad
3268 | const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
3269 |
3270 | // bad
3271 | $.ajax({ method: "POST", url: "https://nulldev.org", data: { foo: "bar" } }).done(() => console.log("Done")).fail(() => console.log("Error"));
3272 |
3273 | // good
3274 | const foo = jsonData
3275 | && jsonData.foo
3276 | && jsonData.foo.bar
3277 | && jsonData.foo.bar.baz
3278 | && jsonData.foo.bar.baz.quux
3279 | && jsonData.foo.bar.baz.quux.xyzzy;
3280 |
3281 | // good
3282 | $.ajax({
3283 | method: "POST",
3284 | url: "https://nulldev.org",
3285 | data: {
3286 | foo: "bar"
3287 | },
3288 | }).done(() => {
3289 | console.log("Done");
3290 | }).fail(() => {
3291 | console.log("Error");
3292 | });
3293 | ```
3294 |
3295 |
3296 | - [19.14](#whitespace--block-spacing) Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line. eslint: [`block-spacing`](https://eslint.org/docs/rules/block-spacing)
3297 |
3298 | ```javascript
3299 | // bad
3300 | function foo(){return true;}
3301 | if (foo){ bar = 0;}
3302 |
3303 | // good
3304 | function foo(){ return true; }
3305 | if (foo){ bar = 0; }
3306 | ```
3307 |
3308 |
3309 | - [19.15](#whitespace--comma-spacing) Avoid spaces before commas and require a space after commas. eslint: [`comma-spacing`](https://eslint.org/docs/rules/comma-spacing)
3310 |
3311 | ```javascript
3312 | // bad
3313 | let foo = 1,bar = 2;
3314 | let arr = [1 , 2];
3315 |
3316 | // good
3317 | let foo = 1, bar = 2;
3318 | let arr = [1, 2];
3319 | ```
3320 |
3321 |
3322 | - [19.16](#whitespace--computed-property-spacing) Enforce spacing inside of computed property brackets. eslint: [`computed-property-spacing`](https://eslint.org/docs/rules/computed-property-spacing)
3323 |
3324 | ```javascript
3325 | // bad
3326 | obj[foo ]
3327 | obj[ "foo"]
3328 | let x = {[ b ]: a}
3329 | obj[foo[ bar ]]
3330 |
3331 | // good
3332 | obj[foo]
3333 | obj["foo"]
3334 | let x = { [b]: a }
3335 | obj[foo[bar]]
3336 | ```
3337 |
3338 |
3339 | - [19.17](#whitespace--func-call-spacing) Avoid spacing between functions and their invocations. eslint: [`func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing)
3340 |
3341 | ```javascript
3342 | // bad
3343 | func ();
3344 |
3345 | func
3346 | ();
3347 |
3348 | // good
3349 | func();
3350 | ```
3351 |
3352 |
3353 | - [19.18](#whitespace--key-spacing) Enforce spacing between keys and values in object literal properties. eslint: [`key-spacing`](https://eslint.org/docs/rules/key-spacing)
3354 |
3355 | ```javascript
3356 | // bad
3357 | let obj = { "foo" : 42 };
3358 | let obj2 = { "foo":42 };
3359 |
3360 | // good
3361 | let obj = { "foo": 42 };
3362 | ```
3363 |
3364 |
3365 | - [19.19](#whitespace--no-trailing-spaces) Avoid trailing spaces at the end of lines. eslint: [`no-trailing-spaces`](https://eslint.org/docs/rules/no-trailing-spaces)
3366 |
3367 |
3368 | - [19.20](#whitespace--no-multiple-empty-lines) Avoid multiple empty lines and only allow one newline at the end of files. eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines)
3369 |
3370 |
3371 | ```javascript
3372 | // bad
3373 | let x = 1;
3374 |
3375 |
3376 |
3377 | let y = 2;
3378 |
3379 | // good
3380 | let x = 1;
3381 |
3382 | let y = 2;
3383 | ```
3384 |
3385 |
3386 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3387 |
3388 | ## Commas
3389 |
3390 |
3391 | - [20.1](#commas--leading-trailing) Do not write leading commas. eslint: [`comma-style`](https://eslint.org/docs/rules/comma-style.html)
3392 |
3393 | ```javascript
3394 | // bad
3395 | const x = [
3396 | foo
3397 | , bar
3398 | , baz
3399 | ];
3400 |
3401 | // good
3402 | const x = [
3403 | foo,
3404 | bar,
3405 | baz,
3406 | ];
3407 |
3408 | // bad
3409 | const x = {
3410 | foo: "foo1"
3411 | , bar: "bar1"
3412 | , baz: "baz1"
3413 | , abc: "abc1"
3414 | };
3415 |
3416 | // good
3417 | const x = {
3418 | foo: "foo1",
3419 | bar: "bar1",
3420 | baz: "baz1",
3421 | abc: "abc1",
3422 | };
3423 | ```
3424 |
3425 |
3426 | - [20.2](#commas--dangling) Write additional trailing commas. eslint: [`comma-dangle`](https://eslint.org/docs/rules/comma-dangle.html)
3427 |
3428 | > Why? It leads to cleaner git diffs and allows easier copy-pasting. **Careful**: A comma must not appear after a [rest element](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). [Read more...](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas)
3429 |
3430 | ```diff
3431 | // git diff without trailing comma (bad)
3432 | const hero = {
3433 | bar: "bar",
3434 | - baz: "baz"
3435 | + baz: "baz",
3436 | + abc: [1, 2, 3]
3437 | };
3438 |
3439 | // git diff with trailing comma (good)
3440 | const foo = {
3441 | bar: "bar",
3442 | baz: "baz",
3443 | + abc: [1, 2, 3],
3444 | };
3445 | ```
3446 |
3447 | ```javascript
3448 | // bad
3449 | const foo = {
3450 | bar: true,
3451 | baz: false
3452 | };
3453 |
3454 | const foo = [
3455 | "bar",
3456 | "baz"
3457 | ];
3458 |
3459 | // good
3460 | const foo = {
3461 | bar: true,
3462 | baz: false,
3463 | };
3464 |
3465 | const foo = [
3466 | "bar",
3467 | "baz",
3468 | ];
3469 |
3470 | // bad
3471 | function foo(
3472 | arg1,
3473 | arg2,
3474 | agr3
3475 | ){
3476 | // ..
3477 | }
3478 |
3479 | // good
3480 | function foo(
3481 | arg1,
3482 | arg2,
3483 | agr3,
3484 | ){
3485 | // ..
3486 | }
3487 |
3488 | // bad
3489 | createUser(
3490 | firstName,
3491 | lastName,
3492 | birthday
3493 | );
3494 |
3495 | // good
3496 | createUser(
3497 | firstName,
3498 | lastName,
3499 | birthday,
3500 | );
3501 | ```
3502 |
3503 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3504 |
3505 | ## Semicolons
3506 |
3507 |
3508 | - [21.1](#semicolons--required) **Use semicolons.** eslint: [`semi`](https://eslint.org/docs/rules/semi.html)
3509 |
3510 | > Why? When JavaScript encounters a line break without a semicolon, it uses a set of rules called [Automatic Semicolon Insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) to determine whether or not it should regard that line break as the end of a statement, and (as the name implies) place a semicolon into your code before the line break if it thinks so. ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break. These rules will become more complicated as new features become a part of JavaScript. Explicitly terminating your statements and configuring your linter to catch missing semicolons will help prevent you from encountering issues. In other words: You could say ASI is a feature to help out, when you _forget_ a semicolon. This doesn't mean you _shouldn't_ use them. Not using semicolons may also slow down the execution because of the additional parsing.
3511 |
3512 | ```javascript
3513 | // bad - raises exception
3514 | const foo = {}
3515 | const bar = {}
3516 | [foo, bar].forEach(baz => baz.x = "test")
3517 |
3518 | // bad - raises exception
3519 | const abc = "Another test"
3520 | (async function tempFoo(){
3521 | // ...
3522 | }())
3523 |
3524 | // bad - returns `undefined` instead of the value on the next line
3525 | // this always happens when `return` is on a line by itself because of ASI!
3526 | function tempBar(){
3527 | return
3528 | "Some string..."
3529 | }
3530 |
3531 | // good
3532 | const foo = {};
3533 | const bar = {};
3534 | [foo, bar].forEach((baz) => {
3535 | baz.x = "test";
3536 | });
3537 |
3538 | // good
3539 | const abc = "Another test";
3540 | (async function tempFoo(){
3541 | // ...
3542 | }());
3543 |
3544 | // good
3545 | function tempBar(){
3546 | return "Some string...";
3547 | }
3548 | ```
3549 |
3550 | [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214#7365214).
3551 |
3552 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3553 |
3554 | ## Type Casting & Coercion
3555 |
3556 |
3557 | - [22.1](#coercion--explicit) Perform type coercion at the beginning of the statement.
3558 |
3559 |
3560 | - [22.2](#coercion--strings) Strings: Prefer [`String()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) over [`.toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString) eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
3561 |
3562 | > Why? `.toString()` is a prototype of `Number`. `String()` on the other hand, is globally available and thus allows casting of any type. Also, `.toString()` can be overridden as seen in [section 9.4](#constructors--tostring)
3563 |
3564 | ```javascript
3565 | // => this.reviewScore = 9;
3566 |
3567 | // bad
3568 | const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
3569 |
3570 | // bad
3571 | const totalScore = this.reviewScore + ""; // invokes this.reviewScore.valueOf()
3572 |
3573 | // bad
3574 | const totalScore = this.reviewScore.toString(); // isn't guaranteed to return a string
3575 |
3576 | // good
3577 | const totalScore = String(this.reviewScore);
3578 | ```
3579 |
3580 |
3581 | - [22.3](#coercion--numbers) Numbers: Use [`Number()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) for type casting and [`parseInt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) only with a radix for parsing strings. Do prefer `Number()` over `parseInt()` though. eslint: [`radix`](https://eslint.org/docs/rules/radix) [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
3582 |
3583 | > Why? Mostly because of the same reasons listed in [the section above](#coercion--strings). Also, since `parseInt()` always expects a string, it does show odd behaviour when parsing very small numbers ([source](https://dmitripavlutin.com/parseint-mystery-javascript/))
3584 |
3585 | ```javascript
3586 | const inputValue = "4";
3587 |
3588 | // bad
3589 | const val = new Number(inputValue);
3590 |
3591 | // bad
3592 | const val = +inputValue;
3593 |
3594 | // bad
3595 | const val = inputValue >> 0;
3596 |
3597 | // bad
3598 | const val = parseInt(inputValue);
3599 |
3600 | // best
3601 | const val = Number(inputValue);
3602 |
3603 | // good
3604 | const val = parseInt(inputValue, 10);
3605 | ```
3606 |
3607 |
3608 | - [22.4](#coercion--comment-deviations) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](https://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing.
3609 |
3610 | ```javascript
3611 | // good
3612 | /**
3613 | * parseInt was the reason my code was slow.
3614 | * Bitshifting the String to coerce it to a
3615 | * Number made it a lot faster.
3616 | */
3617 | const val = inputValue >> 0;
3618 | ```
3619 |
3620 |
3621 | - [22.5](#coercion--bitwise) **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](https://es5.github.io/#x4.3.19), but bitshift operations always return a 32-bit integer ([source](https://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [More info](https://stackoverflow.com/questions/2373791/bitshift-in-javascript). Largest signed 32-bit Int is 2,147,483,647:
3622 |
3623 | ```javascript
3624 | 2147483647 >> 0; // => 2147483647
3625 | 2147483648 >> 0; // => -2147483648
3626 | 2147483649 >> 0; // => -2147483647
3627 | ```
3628 |
3629 |
3630 | - [22.6](#coercion--booleans) Booleans: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
3631 |
3632 | ```javascript
3633 | const age = 0;
3634 |
3635 | // bad
3636 | const hasAge = new Boolean(age);
3637 |
3638 | // good
3639 | const hasAge = Boolean(age);
3640 |
3641 | // best
3642 | const hasAge = !!age;
3643 | ```
3644 |
3645 |
3646 | - [22.6](#coercion--valid-typeof) Only compare returned strings by `typeof` to valid strings: eslint: [`valid-typeof`](https://eslint.org/docs/rules/valid-typeof)
3647 |
3648 | ```javascript
3649 |
3650 | // bad - will be prevented by linter
3651 | typeof foo === "strnig";
3652 |
3653 | // good
3654 | typeof foo === "string";
3655 | typeof bar === typeof foo;
3656 | ```
3657 |
3658 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3659 |
3660 | ## Naming Conventions
3661 |
3662 |
3663 | - [23.0](#naming--language) Stick to the english language. Always write variable names, function names, comments and co in english.
3664 |
3665 | > Why? Some reasons:
3666 | > - Consistency.
3667 | > - English is a global language. What if you're part of a german developer team, write code in german and then want to hire someone from another country?
3668 | > - JavaScript's keywords are english.
3669 | > - Some languages use symbols from different charsets (ö, ä, ü, ß, Ѱ, Ω, etc. pp.). Some of them are illegal as variable/function names and others could break your encoding.
3670 |
3671 |
3672 | - [23.1](#naming--descriptive) Avoid single letter names. Be descriptive with your naming. eslint: [`id-length`](https://eslint.org/docs/rules/id-length)
3673 |
3674 | ```javascript
3675 | // bad
3676 | function q(){
3677 | // ...
3678 | }
3679 |
3680 | // good
3681 | function query(){
3682 | // ...
3683 | }
3684 | ```
3685 |
3686 |
3687 | - [23.2](#naming--camelCase) Use camelCase when naming objects, functions, and instances. eslint: [`camelcase`](https://eslint.org/docs/rules/camelcase.html)
3688 |
3689 | ```javascript
3690 | // bad
3691 | const OBJEcttsssss = {};
3692 | const this_is_my_object = {};
3693 | function c(){}
3694 |
3695 | // good
3696 | const thisIsMyObject = {};
3697 | function thisIsMyFunction(){}
3698 | ```
3699 |
3700 |
3701 | - [23.3](#naming--PascalCase) Use PascalCase only when naming constructors or classes. eslint: [`new-cap`](https://eslint.org/docs/rules/new-cap.html)
3702 |
3703 | ```javascript
3704 | // bad
3705 | function user(options){
3706 | this.name = options.name;
3707 | }
3708 |
3709 | const bad = new user({
3710 | name: "...",
3711 | });
3712 |
3713 | // good
3714 | class User {
3715 | constructor(options){
3716 | this.name = options.name;
3717 | }
3718 | }
3719 |
3720 | const good = new User({
3721 | name: "...",
3722 | });
3723 | ```
3724 |
3725 |
3726 | - [23.4](#naming--leading-underscore) Do not use trailing or leading underscores. eslint: [`no-underscore-dangle`](https://eslint.org/docs/rules/no-underscore-dangle.html)
3727 |
3728 | > Why? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present.
3729 |
3730 | ```javascript
3731 | // bad
3732 | this.__firstName__ = "Foo";
3733 | this.firstName_ = "Foo";
3734 | this._firstName = "Foo";
3735 |
3736 | // good
3737 | this.firstName = "Foo";
3738 |
3739 | // good, in environments where WeakMaps are available
3740 | // see https://kangax.github.io/compat-table/es6/#test-WeakMap
3741 | const firstNames = new WeakMap();
3742 | firstNames.set(this, "Foo");
3743 | ```
3744 |
3745 |
3746 | - [23.5](#naming--self-this) Don’t save references to `this`. Use arrow functions or [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
3747 |
3748 | ```javascript
3749 | // bad
3750 | function foo(){
3751 | const self = this;
3752 | return function(){
3753 | console.log(self);
3754 | };
3755 | }
3756 |
3757 | // bad
3758 | function foo(){
3759 | const that = this;
3760 | return function(){
3761 | console.log(that);
3762 | };
3763 | }
3764 |
3765 | // good
3766 | function foo(){
3767 | return () => {
3768 | console.log(this);
3769 | };
3770 | }
3771 | ```
3772 |
3773 |
3774 | - [23.6](#naming--filename-matches-export) A base filename should exactly match the name of its default export.
3775 |
3776 | ```javascript
3777 | // file 1 contents
3778 | class CheckBox {
3779 | // ...
3780 | }
3781 | module.exports = CheckBox;
3782 |
3783 | // file 2 contents
3784 | module.exports = function fortyTwo(){ return 42; }
3785 |
3786 | // file 3 contents
3787 | module.exports = function insideDirectory(){}
3788 |
3789 | // in some other file
3790 | // bad
3791 | let CheckBox = require("./checkBox"); // PascalCase import/export, camelCase filename
3792 | let FortyTwo = require("./FortyTwo"); // PascalCase import/filename, camelCase export
3793 | let InsideDirectory = require("./InsideDirectory"); // PascalCase import/filename, camelCase export
3794 |
3795 | // bad
3796 | let CheckBox = require("./check_box"); // PascalCase import/export, snake_case filename
3797 | let forty_two = require("./forty_two"); // snake_case import/filename, camelCase export
3798 | let inside_directory = require("./inside_directory"); // snake_case import, camelCase export
3799 | let index = require("./inside_directory/index"); // requiring the index file explicitly
3800 | let insideDirectory = require("./insideDirectory/index"); // requiring the index file explicitly
3801 |
3802 | // good
3803 | let CheckBox = require("./CheckBox"); // PascalCase export/import/filename
3804 | let fortyTwo = require("./fortyTwo"); // camelCase export/import/filename
3805 | let insideDirectory = require("./insideDirectory"); // camelCase export/import/directory name/implicit "index"
3806 | // ^ supports both insideDirectory.js and insideDirectory/index.js
3807 | ```
3808 |
3809 |
3810 | - [23.7](#naming--camelCase-default-export) Use camelCase when you export-default a function. Your filename should be identical to your function’s name.
3811 |
3812 | ```javascript
3813 | function makeStyleGuide(){
3814 | // ...
3815 | }
3816 |
3817 | module.exports = makeStyleGuide;
3818 | ```
3819 |
3820 |
3821 | - [23.8](#naming--PascalCase-singleton) Use PascalCase when you export a constructor / class / singleton / function library / bare object.
3822 |
3823 | ```javascript
3824 | const Foo = {
3825 | bar: {
3826 | }
3827 | };
3828 |
3829 | module.exports = Foo;
3830 | ```
3831 |
3832 |
3833 | - [23.9](#naming--Acronyms-and-Initialisms) Acronyms and initialisms should always be all capitalized, or all lowercased.
3834 |
3835 | > Why? Names are for readability, not to appease a computer algorithm.
3836 |
3837 | ```javascript
3838 | // bad
3839 | let SmsContainer = require("./containers/SmsContainer");
3840 |
3841 | // bad
3842 | const HttpRequests = [
3843 | // ...
3844 | ];
3845 |
3846 | // good
3847 | let SMSContainer = require("./containers/SMSContainer");
3848 |
3849 | // good
3850 | const HTTPRequests = [
3851 | // ...
3852 | ];
3853 |
3854 | // also good (sticks to camelCase)
3855 | const httpRequests = [
3856 | // ...
3857 | ];
3858 |
3859 | // best
3860 | let TextMessageContainer = require("./containers/TextMessageContainer");
3861 |
3862 | // best
3863 | const requests = [
3864 | // ...
3865 | ];
3866 | ```
3867 |
3868 |
3869 | - [23.10](#naming--uppercase) You may optionally uppercase a constant only if it (1) is exported, (2) is a `const` (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.
3870 | **Note**: This is about ES6 import/export, **not** CommonJS require()
3871 |
3872 | > Why? This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change.
3873 | - What about all `const` variables? This is unnecessary, so uppercasing should not be used for constants within a file. It should be used for exported constants however.
3874 | - What about exported objects? Uppercase at the top level of export (e.g. `EXPORTED_OBJECT.key`) and maintain that all nested properties do not change.
3875 |
3876 | ```javascript
3877 | // bad
3878 | const PRIVATE_VARIABLE = "should not be unnecessarily uppercased within a file";
3879 |
3880 | // bad
3881 | export const THING_TO_BE_CHANGED = "should obviously not be uppercased";
3882 |
3883 | // bad
3884 | export let REASSIGNABLE_VARIABLE = "do not use let with uppercase variables";
3885 |
3886 | // ---
3887 |
3888 | // allowed but does not supply semantic value
3889 | export const apiKey = "SOMEKEY";
3890 |
3891 | // better in most cases
3892 | export const API_KEY = "SOMEKEY";
3893 |
3894 | // ---
3895 |
3896 | // bad - unnecessarily uppercases key while adding no semantic value
3897 | export const MAPPING = {
3898 | KEY: "value",
3899 | };
3900 |
3901 | // good
3902 | export const MAPPING = {
3903 | key: "value",
3904 | };
3905 | ```
3906 |
3907 | - [23.11](#naming--state-booleans) Write `isCondition` instead of `conditionState` for boolean state checks.
3908 |
3909 | > Why? It makes the intentions clear.
3910 |
3911 | ```javascript
3912 | // bad - this is very vague
3913 | if (activeState) doSomething();
3914 |
3915 | // bad - the "=== true" check makes the intentions clear, but is also unnecessary
3916 | if (activeState === true) doSomething();
3917 |
3918 | // good - short and clear
3919 | if (isActive) doSomething();
3920 | ```
3921 |
3922 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3923 |
3924 | ## Accessors
3925 |
3926 |
3927 | - [24.1](#accessors--not-required) Accessor functions for properties are not required.
3928 |
3929 |
3930 | - [24.2](#accessors--no-getters-setters) Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use `getVal()` and `setVal("foo")`.
3931 |
3932 | ```javascript
3933 | // bad
3934 | class Foo {
3935 | get bar(){
3936 | // ...
3937 | }
3938 |
3939 | set bar(value){
3940 | // ...
3941 | }
3942 | }
3943 |
3944 | // good
3945 | class Foo {
3946 | getBar(){
3947 | // ...
3948 | }
3949 |
3950 | setBar(value){
3951 | // ...
3952 | }
3953 | }
3954 | ```
3955 |
3956 |
3957 | - [24.3](#accessors--boolean-prefix) If the property/method is a `boolean`, use `isVal()` or `hasVal()`.
3958 |
3959 | ```javascript
3960 | // bad
3961 | if (!foo.bar()){
3962 | return false;
3963 | }
3964 |
3965 | // good
3966 | if (!foo.hasBar()){
3967 | return false;
3968 | }
3969 | ```
3970 |
3971 |
3972 | - [24.4](#accessors--consistent) It’s okay to create `get()` and `set()` functions, but be consistent.
3973 |
3974 | ```javascript
3975 | class Foo {
3976 | constructor(options = {}){
3977 | const bar = options.bar || "Default bar";
3978 | this.set("bar", bar);
3979 | }
3980 |
3981 | set(key, val){
3982 | this[key] = val;
3983 | }
3984 |
3985 | get(key){
3986 | return this[key];
3987 | }
3988 | }
3989 | ```
3990 |
3991 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
3992 |
3993 | ## Events
3994 |
3995 |
3996 | - [25.1](#events--hash) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass an object literal (also known as a "hash") instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of:
3997 |
3998 | ```javascript
3999 | // bad
4000 | $(this).trigger("listingUpdated", listing.id);
4001 |
4002 | // ...
4003 |
4004 | $(this).on("listingUpdated", (e, listingID) => {
4005 | // do something with listingID
4006 | });
4007 | ```
4008 |
4009 | prefer:
4010 |
4011 | ```javascript
4012 | // good
4013 | $(this).trigger("listingUpdated", {
4014 | listingID: listing.id
4015 | });
4016 |
4017 | // ...
4018 |
4019 | $(this).on("listingUpdated", (e, data) => {
4020 | // do something with data.listingID
4021 | });
4022 | ```
4023 |
4024 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4025 |
4026 | ## Standard Library
4027 |
4028 | The [Standard Library](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)
4029 | contains utilities that are functionally broken but remain for legacy reasons.
4030 |
4031 |
4032 | - [26.1](#standard-library--isnan) Use `Number.isNaN` instead of global `isNaN`.
4033 | eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
4034 |
4035 | > Why? The global `isNaN` coerces non-numbers to numbers, returning true for anything that coerces to NaN.
4036 | > If this behavior is desired, make it explicit.
4037 |
4038 | ```javascript
4039 | // bad
4040 | isNaN("1.2"); // false
4041 | isNaN("1.2.3"); // true
4042 |
4043 | // good
4044 | Number.isNaN("1.2.3"); // false
4045 | Number.isNaN(Number("1.2.3")); // true
4046 | ```
4047 |
4048 |
4049 | - [26.2](#standard-library--isfinite) Use `Number.isFinite` instead of global `isFinite`.
4050 | eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
4051 |
4052 | > Why? The global `isFinite` coerces non-numbers to numbers, returning true for anything that coerces to a finite number.
4053 | > If this behavior is desired, make it explicit.
4054 |
4055 | ```javascript
4056 | // bad
4057 | isFinite("2e3"); // true
4058 |
4059 | // good
4060 | Number.isFinite("2e3"); // false
4061 | Number.isFinite(parseInt("2e3", 10)); // true
4062 | ```
4063 |
4064 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4065 |
4066 | ## jQuery
4067 |
4068 |
4069 | - [27.1](#jquery--dollar-prefix) Prefix jQuery object variables with a `$`.
4070 |
4071 | ```javascript
4072 | // bad
4073 | const sidebar = $(".sidebar");
4074 |
4075 | // good
4076 | const $sidebar = $(".sidebar");
4077 |
4078 | // good
4079 | const $sidebarBtn = $(".sidebar-btn");
4080 | ```
4081 |
4082 |
4083 | - [27.2](#jquery--cache) Cache jQuery lookups.
4084 |
4085 | ```javascript
4086 | // bad
4087 | function setSidebar(){
4088 | $(".sidebar").hide();
4089 |
4090 | // ...
4091 |
4092 | $(".sidebar").css({
4093 | "background-color": "green",
4094 | });
4095 | }
4096 |
4097 | // good
4098 | function setSidebar(){
4099 | const $sidebar = $(".sidebar");
4100 | $sidebar.hide();
4101 |
4102 | // ...
4103 |
4104 | $sidebar.css({
4105 | "background-color": "green",
4106 | });
4107 | }
4108 | ```
4109 |
4110 |
4111 | - [27.3](#jquery--queries) For DOM queries use Cascading `$(".sidebar ul")` or parent > child `$(".sidebar > ul")`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
4112 |
4113 |
4114 | - [27.4](#jquery--find) Use `find` with scoped jQuery object queries.
4115 |
4116 | ```javascript
4117 | // bad
4118 | $("ul", ".sidebar").hide();
4119 |
4120 | // bad
4121 | $(".sidebar").find("ul").hide();
4122 |
4123 | // good
4124 | $(".sidebar ul").hide();
4125 |
4126 | // good
4127 | $(".sidebar > ul").hide();
4128 |
4129 | // good
4130 | $sidebar.find("ul").hide();
4131 | ```
4132 |
4133 |
4134 | - [27.5](#jquery--on) Use `.on` instead of the event name (shorthands) when doing bindings.
4135 |
4136 | > Why? `.on()` works on dynamically added elements and is better in performance. [Read more...](https://stackoverflow.com/questions/9122078/difference-between-onclick-vs-click)
4137 |
4138 | ```javascript
4139 | // bad
4140 | $("a.test").click(function(){
4141 | // ...
4142 | });
4143 |
4144 | $("div.test").mouseover(function(){
4145 | // ...
4146 | });
4147 |
4148 | // good
4149 | $("a.test").on("click", function(){
4150 | // ...
4151 | });
4152 |
4153 | $("div.test").on("mouseover", function(){
4154 | // ...
4155 | });
4156 | ```
4157 |
4158 |
4159 | - [27.6](#jquery--ready) Don't bloat the `$(document).ready()` function
4160 |
4161 | > Why? It actively harms readability and generally the structure of the code.
4162 |
4163 | ```javascript
4164 |
4165 | // Bad
4166 | $(document).ready(function(){
4167 | $(foo).on("click", function(){
4168 | doStuff();
4169 | doMoreStuff();
4170 | doEvenMoreStuff(function(stuff){
4171 | // ...
4172 | });
4173 | });
4174 |
4175 | $(bar).on("click", function(){
4176 | doMoreStuff();
4177 | doStuff();
4178 | });
4179 |
4180 | $(baz).on("click", function(){
4181 | doMoreStuff();
4182 | doEvenMoreStuff(function(stuff){
4183 | // ...
4184 | });
4185 | });
4186 | });
4187 |
4188 | // Good
4189 | function stuffHandler(){
4190 | // ...
4191 | }
4192 |
4193 | $(document).ready(function(){
4194 | $(foo).on("click", stuffHandler);
4195 | $(bar).on("click", stuffHandler);
4196 | $(baz).on("click", stuffHandler);
4197 | });
4198 | ```
4199 |
4200 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4201 |
4202 | ## ECMAScript 5 Compatibility
4203 |
4204 |
4205 | - [28.1](#es5-compat--kangax) Refer to [Kangax](https://twitter.com/kangax/)’s ES5 [compatibility table](https://kangax.github.io/es5-compat-table/).
4206 |
4207 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4208 |
4209 |
4210 | ## ECMAScript 6+ (ES 2015+) Styles
4211 |
4212 |
4213 | - [29.1](#es6-styles) This is a collection of links to the various ES6+ features.
4214 |
4215 | 1. [Arrow Functions](#arrow-functions)
4216 | 1. [Classes](#classes--constructors)
4217 | 1. [Object Shorthand](#es6-object-shorthand)
4218 | 1. [Object Concise](#es6-object-concise)
4219 | 1. [Object Computed Properties](#es6-computed-properties)
4220 | 1. [Template Strings](#es6-template-literals)
4221 | 1. [Destructuring](#destructuring)
4222 | 1. [Default Parameters](#es6-default-parameters)
4223 | 1. [Rest](#es6-rest)
4224 | 1. [Array Spreads](#es6-array-spreads)
4225 | 1. [Let and Const](#references)
4226 | 1. [Exponentiation Operator](#es2016-properties--exponentiation-operator)
4227 | 1. [Iterators and Generators](#iterators-and-generators)
4228 | 1. [Modules](#modules)
4229 |
4230 |
4231 | - [29.2](#tc39-proposals) Do not use [TC39 proposals](https://github.com/tc39/proposals) that have not reached stage 3.
4232 |
4233 | > Why? [They are not finalized](https://tc39.github.io/process-document/), and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet.
4234 |
4235 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4236 |
4237 | ## Testing
4238 |
4239 |
4240 | - [30.1](#testing--tests) **Tests**
4241 | - Whichever testing framework you use, you should be writing tests!
4242 | - Strive to write many small pure functions, and minimize where mutations occur.
4243 | - Be cautious about stubs and mocks - they can make your tests more brittle.
4244 | - Recommendations: [`mocha`](https://www.npmjs.com/package/mocha) and [`jest`](https://www.npmjs.com/package/jest) or [`tape`](https://www.npmjs.com/package/tape) for small, separate modules.
4245 | - 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.
4246 | - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future.
4247 |
4248 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4249 |
4250 | ## Performance
4251 |
4252 |
4253 | - [31.1](#performance--performance) **Performance**
4254 | - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/)
4255 | - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2)
4256 | - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost)
4257 | - [Bang Function](https://jsperf.com/bang-function)
4258 | - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13)
4259 | - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text)
4260 | - [Long String Concatenation](https://jsperf.com/ya-string-concat)
4261 | - [Are JavaScript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta)
4262 |
4263 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4264 |
4265 | ## Resources
4266 |
4267 |
4268 | - [32.1](#resources--learning-es6) **Learning ES6+**
4269 | - [Latest ECMA spec](https://tc39.github.io/ecma262/)
4270 | - [ExploringJS](http://exploringjs.com/)
4271 | - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/)
4272 | - [Comprehensive Overview of ES6 Features](http://es6-features.org/)
4273 | - [More on ES6 Features](https://github.com/lukehoban/es6features/)
4274 | - [Useful JavaScript snippets that you can understand in 30 seconds](https://github.com/30-seconds/30-seconds-of-code)
4275 | - [Common JS interview questions (test yourself here)](https://github.com/30-seconds/30-seconds-of-interviews)
4276 | - [Common JS Algorithms and Data Structures](https://github.com/trekhleb/javascript-algorithms)
4277 |
4278 |
4279 | - [32.2](#resources--read-this) **Read This**
4280 | - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html)
4281 | - [NodeJS Best practices](https://github.com/goldbergyoni/nodebestpractices)
4282 |
4283 |
4284 | - [32.3](#resources--tools) **Tools**
4285 | - Code Style Linters
4286 | - [ESlint](https://eslint.org/) - [NullDev Style .eslintrc](https://github.com/NullDev/JavaScript-Styleguide/blob/master/.eslintrc)
4287 |
4288 |
4289 | - [32.4](#resources--further-reading) **Further Reading**
4290 | - [Understanding JavaScript Closures](https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll
4291 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer
4292 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz
4293 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban
4294 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock
4295 |
4296 |
4297 | - [32.5](#resources--books) **Books**
4298 | - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford
4299 | - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov
4300 | - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz
4301 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders
4302 | - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas
4303 | - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw
4304 | - [Pro JavaScript Techniques](https://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig
4305 | - [Smashing Node.js: JavaScript Everywhere](https://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch
4306 | - [Secrets of the JavaScript Ninja](https://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault
4307 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg
4308 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
4309 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon
4310 | - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov
4311 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman
4312 | - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke
4313 | - [You Don’t Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson
4314 |
4315 |
4316 | - [32.6](#resources--blogs) **Blogs**
4317 | - [JavaScript Weekly](http://javascriptweekly.com/)
4318 | - [JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/)
4319 | - [Bocoup Weblog](https://bocoup.com/weblog)
4320 | - [Adequately Good](http://www.adequatelygood.com/)
4321 | - [NCZOnline](https://www.nczonline.net/)
4322 | - [Perfection Kills](http://perfectionkills.com/)
4323 | - [Ben Alman](http://benalman.com/)
4324 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/)
4325 | - [nettuts](http://code.tutsplus.com/?s=javascript)
4326 |
4327 |
4328 | - [32.7](#resources--podcasts) **Podcasts**
4329 | - [JavaScript Air](https://javascriptair.com/)
4330 | - [JavaScript Jabber](https://devchat.tv/js-jabber/)
4331 |
4332 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4333 |
4334 | ## Copyright
4335 |
4336 |
4337 | - [33.1](#copyright--base) This Styleguide is based on [AirBnB's JavaScript Styleguide](https://github.com/airbnb/javascript)
4338 |
4339 |
4340 | - [33.2](#copyright--license) It uses the same [License](https://github.com/NullDev/JavaScript-Styleguide/blob/master/LICENSE).
4341 |
4342 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4343 |
4344 | ## Amendments
4345 |
4346 |
4347 | - [34.1](#amendments--forking) We encourage you to fork this guide and change the rules to fit your team’s style guide. :smile_cat:
4348 |
4349 | **[⬆ back to top](#table-of-contents-bookmark_tabs)**
4350 |
4351 |
4352 |
4353 |
4354 |
4355 |
4356 |
4357 |
4358 |
4359 | [](https://nulldev.org)
4360 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import babelParser from "@babel/eslint-parser";
3 |
4 | export default [{ // NullDev-Style ESLint Config: https://github.com/NullDevCo/JavaScript-Styleguide
5 | ignores: [ // Ignore dist folders and dependencies
6 | "dist", "node_modules",
7 | ],
8 | files: ["**/*.js", "**/*.jsx"],
9 | plugins: {}, // Additional ESLint Plugins
10 | languageOptions: {
11 | parser: babelParser, // npm install @babel/eslint-parser @babel/core eslint --save-dev
12 | ecmaVersion: "latest", // set highest possible version
13 | sourceType: "module", // prefer ES Modules (doesn't require "use strict")
14 | parserOptions: {
15 | requireConfigFile: false, // make babel not look for config
16 | babelOptions: {
17 | presets: [ // If used with React, add "@babel/preset-react" here and do `npm i -D @babel/preset-react`
18 | ],
19 | plugins: [ // additional plugins for new ES-proposals such as "@babel/plugin-proposal-class-properties"
20 | ],
21 | },
22 | },
23 | globals: { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments
24 | browser: true, // browser global variables
25 | node: true, // Node.js global variables and Node.js-specific rules
26 | commonjs: true, // CommonJS global variables and CommonJS scoping
27 | es2022: true, // ESNext support
28 | es6: true, // enable all ECMAScript 6 features except for modules
29 | },
30 | },
31 | rules: {
32 | /**
33 | * Strict mode
34 | */
35 | strict: [2, "global"], // http://eslint.org/docs/rules/strict
36 | /**
37 | * ES6
38 | */
39 | "no-var": 2, // http://eslint.org/docs/rules/no-var
40 | "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const
41 | "prefer-destructuring": [2, { // http://eslint.org/docs/rules/prefer-destructuring
42 | array: false,
43 | object: true,
44 | }, {
45 | enforceForRenamedProperties: false,
46 | }],
47 | /**
48 | * Variables
49 | */
50 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
51 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
52 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
53 | vars: "local",
54 | args: "after-used",
55 | }],
56 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
57 | /**
58 | * Possible errors
59 | */
60 | "comma-dangle": [ // http://eslint.org/docs/rules/comma-dangle
61 | 2,
62 | "always-multiline",
63 | ],
64 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
65 | "no-console": 0, // http://eslint.org/docs/rules/no-console
66 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
67 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert
68 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
69 | "no-const-assign": 2, // http://eslint.org/docs/rules/no-const-assign
70 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
71 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
72 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty
73 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
74 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
75 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
76 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
77 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
78 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
79 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
80 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
81 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
82 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
83 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
84 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
85 | "valid-typeof": 2, // http://eslint.org/docs/rules/valid-typeof
86 | /**
87 | * Best practices
88 | */
89 | "array-callback-return": [2, { // http://eslint.org/docs/rules/array-callback-return
90 | allowImplicit: true,
91 | }],
92 | "consistent-return": 1, // http://eslint.org/docs/rules/consistent-return
93 | curly: [2, "multi-line"], // http://eslint.org/docs/rules/curly
94 | "default-case": 2, // http://eslint.org/docs/rules/default-case
95 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
96 | allowKeywords: true,
97 | }],
98 | "linebreak-style": [2, "unix"], // http://eslint.org/docs/rules/linebreak-style
99 | eqeqeq: 2, // http://eslint.org/docs/rules/eqeqeq
100 | "guard-for-in": 0, // http://eslint.org/docs/rules/guard-for-in
101 | "no-array-constructor": 2, // http://eslint.org/docs/rules/no-array-constructor
102 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller
103 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
104 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
105 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval
106 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
107 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
108 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
109 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
110 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
111 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
112 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
113 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
114 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
115 | "no-new": 2, // http://eslint.org/docs/rules/no-new
116 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
117 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
118 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal
119 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
120 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
121 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto
122 | "no-prototype-builtins": 1, // http://eslint.org/docs/rules/no-prototype-builtins
123 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
124 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
125 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
126 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
127 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
128 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
129 | "no-with": 2, // http://eslint.org/docs/rules/no-with
130 | radix: 2, // http://eslint.org/docs/rules/radix
131 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
132 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
133 | "object-shorthand": [2, "always", { // http://eslint.org/docs/rules/object-shorthand
134 | ignoreConstructors: true,
135 | avoidQuotes: true,
136 | }],
137 | "quote-props": [2, "as-needed", { // http://eslint.org/docs/rules/quote-props
138 | keywords: true,
139 | }],
140 | yoda: 2, // http://eslint.org/docs/rules/yoda
141 | /**
142 | * Style
143 | */
144 | indent: [2, 4, { // http://eslint.org/docs/rules/indent
145 | SwitchCase: 1,
146 | }],
147 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style
148 | "stroustrup", {
149 | allowSingleLine: true,
150 | },
151 | ],
152 | quotes: [ // http://eslint.org/docs/rules/quotes
153 | 2, "double", "avoid-escape",
154 | ],
155 | camelcase: [2, { // http://eslint.org/docs/rules/camelcase
156 | properties: "never",
157 | }],
158 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
159 | before: false,
160 | after: true,
161 | }],
162 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
163 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last
164 | "func-names": 0, // http://eslint.org/docs/rules/func-names
165 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
166 | beforeColon: false,
167 | afterColon: true,
168 | }],
169 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap
170 | newIsCap: true,
171 | }],
172 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
173 | max: 2,
174 | }],
175 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
176 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
177 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
178 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
179 | "no-extra-parens": [2, // http://eslint.org/docs/rules/no-extra-parens
180 | "functions",
181 | ],
182 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
183 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
184 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
185 | semi: [2, "always"], // http://eslint.org/docs/rules/semi
186 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
187 | before: false,
188 | after: true,
189 | }],
190 | "space-after-keywords": 0, // http://eslint.org/docs/rules/space-after-keywords
191 | "space-before-blocks": [2, { // http://eslint.org/docs/rules/space-before-blocks
192 | functions: "never",
193 | keywords: "never",
194 | classes: "always",
195 | }],
196 | "keyword-spacing": [0, { // http://eslint.org/docs/rules/keyword-spacing
197 | before: false,
198 | after: true,
199 | }],
200 | "space-before-function-paren": [2, // http://eslint.org/docs/rules/space-before-function-paren
201 | "never",
202 | ],
203 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
204 | "space-return-throw-case": 0, // http://eslint.org/docs/rules/space-return-throw-case
205 | "spaced-comment": 2, // http://eslint.org/docs/rules/spaced-comment
206 | },
207 | }];
208 |
--------------------------------------------------------------------------------
/nulldev-template/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/nulldev-template/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/nulldev-template/.github/ISSUE_TEMPLATE/Bug_Report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Describe a bug you encountered
4 |
5 | ---
6 |
7 | ## Bug Report
8 |
9 | **Description of the bug:**
10 | A detailed description of the bug...
11 |
12 | **Steps to reproduce:**
13 | How can the bug be reproduced?
14 |
15 | 1. Go to '...'
16 | 2. Write '....'
17 | 3. Click on '....'
18 | 4. See stacktrace
19 |
20 | **Expected result:**
21 | What was supposed to happen?
22 |
23 | **Screenshots:**
24 | If available, include screenshots of the error here
25 |
26 | **Environment:**
27 | - Operating system: [e.g. Ubuntu 16.04]
28 | - NodeJS Version: [e.g. v15.14.0] $ node -v
29 | - NPM Version: [e.g. 7.7.6] $ npm -v
30 |
31 | **Additional context**
32 | Any additional info goes here
33 |
--------------------------------------------------------------------------------
/nulldev-template/.github/ISSUE_TEMPLATE/Feature_Request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: What feature do you want implemented?
4 |
5 | ---
6 |
7 | ## Feature Request
8 |
9 | **Functionality:**
10 | What feature do you want implemented and what is it supposed to do?
11 | Please explain in detail.
12 |
--------------------------------------------------------------------------------
/nulldev-template/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Config
10 | config.json
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 |
81 | # Next.js build output
82 | .next
83 |
84 | # Nuxt.js build / generate output
85 | .nuxt
86 | dist
87 |
88 | # Gatsby files
89 | .cache/
90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
91 | # https://nextjs.org/blog/next-9-1#public-directory-support
92 | # public
93 |
94 | # vuepress build output
95 | .vuepress/dist
96 |
97 | # Serverless directories
98 | .serverless/
99 |
100 | # FuseBox cache
101 | .fusebox/
102 |
103 | # DynamoDB Local files
104 | .dynamodb/
105 |
106 | # TernJS port file
107 | .tern-port
108 |
--------------------------------------------------------------------------------
/nulldev-template/README.md:
--------------------------------------------------------------------------------
1 | # My awesome project
2 |
3 | ## Setup :gear:
4 |
5 | - Clone Repo
6 | - `npm i`
7 | - Done!
8 |
9 | ---
10 |
11 | Based on the [NullDev project template](https://github.com/NullDev/JavaScript-Styleguide/tree/master/nulldev-template) and [NullDev Styleguide](https://github.com/NullDev/JavaScript-Styleguide)
12 |
--------------------------------------------------------------------------------
/nulldev-template/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import babelParser from "@babel/eslint-parser";
3 |
4 | export default [{ // NullDev-Style ESLint Config: https://github.com/NullDevCo/JavaScript-Styleguide
5 | ignores: ["dist", "node_modules"], // Ignore dist folders and dependencies
6 | files: ["**/*.js", "**/*.jsx"],
7 | plugins: {}, // Additional ESLint Plugins
8 | languageOptions: {
9 | parser: babelParser, // npm install @babel/eslint-parser @babel/core eslint --save-dev
10 | ecmaVersion: "latest", // set highest possible version
11 | sourceType: "module", // prefer ES Modules (doesn't require "use strict")
12 | parserOptions: {
13 | requireConfigFile: false, // make babel not look for config
14 | babelOptions: {
15 | plugins: [ // additional plugins for new ES-proposals such as "@babel/plugin-proposal-class-properties"
16 | ],
17 | },
18 | },
19 | globals: { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments
20 | browser: true, // browser global variables
21 | node: true, // Node.js global variables and Node.js-specific rules
22 | commonjs: true, // CommonJS global variables and CommonJS scoping
23 | es2022: true, // ESNext support
24 | es6: true, // enable all ECMAScript 6 features except for modules
25 | },
26 | },
27 | rules: {
28 | /**
29 | * Strict mode
30 | */
31 | strict: [2, "global"], // http://eslint.org/docs/rules/strict
32 | /**
33 | * ES6
34 | */
35 | "no-var": 2, // http://eslint.org/docs/rules/no-var
36 | "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const
37 | "prefer-destructuring": [2, { // http://eslint.org/docs/rules/prefer-destructuring
38 | array: false,
39 | object: true,
40 | }, {
41 | enforceForRenamedProperties: false,
42 | }],
43 | /**
44 | * Variables
45 | */
46 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
47 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
48 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
49 | vars: "local",
50 | args: "after-used",
51 | }],
52 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
53 | /**
54 | * Possible errors
55 | */
56 | "comma-dangle": [ // http://eslint.org/docs/rules/comma-dangle
57 | 2,
58 | "always-multiline",
59 | ],
60 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
61 | "no-console": 0, // http://eslint.org/docs/rules/no-console
62 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
63 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert
64 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
65 | "no-const-assign": 2, // http://eslint.org/docs/rules/no-const-assign
66 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
67 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
68 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty
69 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
70 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
71 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
72 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
73 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
74 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
75 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
76 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
77 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
78 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
79 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
80 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
81 | "valid-typeof": 2, // http://eslint.org/docs/rules/valid-typeof
82 | /**
83 | * Best practices
84 | */
85 | "array-callback-return": [2, { // http://eslint.org/docs/rules/array-callback-return
86 | allowImplicit: true,
87 | }],
88 | "consistent-return": 1, // http://eslint.org/docs/rules/consistent-return
89 | curly: [2, "multi-line"], // http://eslint.org/docs/rules/curly
90 | "default-case": 2, // http://eslint.org/docs/rules/default-case
91 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
92 | allowKeywords: true,
93 | }],
94 | "linebreak-style": [2, "unix"], // http://eslint.org/docs/rules/linebreak-style
95 | eqeqeq: 2, // http://eslint.org/docs/rules/eqeqeq
96 | "guard-for-in": 0, // http://eslint.org/docs/rules/guard-for-in
97 | "no-array-constructor": 2, // http://eslint.org/docs/rules/no-array-constructor
98 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller
99 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
100 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
101 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval
102 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
103 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
104 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
105 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
106 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
107 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
108 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
109 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
110 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
111 | "no-new": 2, // http://eslint.org/docs/rules/no-new
112 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
113 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
114 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal
115 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
116 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
117 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto
118 | "no-prototype-builtins": 1, // http://eslint.org/docs/rules/no-prototype-builtins
119 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
120 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
121 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
122 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
123 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
124 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
125 | "no-with": 2, // http://eslint.org/docs/rules/no-with
126 | radix: 2, // http://eslint.org/docs/rules/radix
127 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
128 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
129 | "object-shorthand": [2, "always", { // http://eslint.org/docs/rules/object-shorthand
130 | ignoreConstructors: true,
131 | avoidQuotes: true,
132 | }],
133 | "quote-props": [2, "as-needed", { // http://eslint.org/docs/rules/quote-props
134 | keywords: true,
135 | }],
136 | yoda: 2, // http://eslint.org/docs/rules/yoda
137 | /**
138 | * Style
139 | */
140 | indent: [2, 4, { // http://eslint.org/docs/rules/indent
141 | SwitchCase: 1,
142 | }],
143 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style
144 | "stroustrup", {
145 | allowSingleLine: true,
146 | },
147 | ],
148 | quotes: [
149 | 2, "double", "avoid-escape", // http://eslint.org/docs/rules/quotes
150 | ],
151 | camelcase: [2, { // http://eslint.org/docs/rules/camelcase
152 | properties: "never",
153 | }],
154 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
155 | before: false,
156 | after: true,
157 | }],
158 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
159 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last
160 | "func-names": 0, // http://eslint.org/docs/rules/func-names
161 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
162 | beforeColon: false,
163 | afterColon: true,
164 | }],
165 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap
166 | newIsCap: true,
167 | }],
168 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
169 | max: 2,
170 | }],
171 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
172 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
173 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
174 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
175 | "no-extra-parens": [2,
176 | "functions", // http://eslint.org/docs/rules/no-extra-parens
177 | ],
178 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
179 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
180 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
181 | semi: [2, "always"], // http://eslint.org/docs/rules/semi
182 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
183 | before: false,
184 | after: true,
185 | }],
186 | "space-after-keywords": 0, // http://eslint.org/docs/rules/space-after-keywords
187 | "space-before-blocks": [2, { // http://eslint.org/docs/rules/space-before-blocks
188 | functions: "never",
189 | keywords: "never",
190 | classes: "always",
191 | }],
192 | "keyword-spacing": [0, { // http://eslint.org/docs/rules/keyword-spacing
193 | before: false,
194 | after: true,
195 | }],
196 | "space-before-function-paren": [2,
197 | "never", // http://eslint.org/docs/rules/space-before-function-paren
198 | ],
199 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
200 | "space-return-throw-case": 0, // http://eslint.org/docs/rules/space-return-throw-case
201 | "spaced-comment": 2, // http://eslint.org/docs/rules/spaced-comment
202 | },
203 | }];
204 |
--------------------------------------------------------------------------------
/nulldev-template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nulldev-template",
3 | "version": "1.0.0",
4 | "description": "A NodeJS Project template",
5 | "main": "src/app.js",
6 | "scripts": {
7 | "lint": "eslint ./src",
8 | "lint:fix": "eslint ./src --fix",
9 | "start": "npm run lint && NODE_ENV=development node src/app.js",
10 | "prod": "NODE_ENV=production node ./src/app.js",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [],
14 | "author": "NullDev",
15 | "license": "UNLICENSED",
16 | "devDependencies": {
17 | "@babel/core": "^7.24.5",
18 | "@babel/eslint-parser": "^7.24.5",
19 | "eslint": "^9.3.0"
20 | },
21 | "engines": {
22 | "node": ">=15.0.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/nulldev-template/src/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Utils
4 | let conf = require("./utils/configHandler");
5 | let log = require("./utils/logger");
6 |
7 | const version = conf.getVersion();
8 | const appname = conf.getName();
9 | const splashPadding = 12 + appname.length + version.toString().length;
10 |
11 | console.log(
12 | `\n #${"-".repeat(splashPadding)}#\n` +
13 | ` # Started ${appname} v${version} #\n` +
14 | ` #${"-".repeat(splashPadding)}#\n\n` +
15 | ` Copyright (c) ${(new Date()).getFullYear()} ${conf.getAuthor()}\n`
16 | );
17 |
18 | const config = conf.getConfig();
19 | log.done(`Started. Loaded ${Object.keys(config).length} config values.`);
20 |
21 | // ...
22 |
--------------------------------------------------------------------------------
/nulldev-template/src/utils/configHandler.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // ========================= //
4 | // = Copyright (c) NullDev = //
5 | // ========================= //
6 |
7 | // Core Modules
8 | let fs = require("fs");
9 | let path = require("path");
10 |
11 | // Utils
12 | let log = require("./logger");
13 |
14 | const packagefile = require(path.resolve("package.json"));
15 | const configPath = path.resolve("config.json");
16 |
17 | /**
18 | * Check if the config is valid JSON
19 | *
20 | * @param {*} obj
21 | * @returns {Boolean} whether it is valid JSON
22 | */
23 | let validJson = function(obj){
24 | try {
25 | JSON.parse(obj);
26 | }
27 | catch (e){
28 | return false;
29 | }
30 | return true;
31 | };
32 |
33 | /**
34 | * Reads out config data
35 | *
36 | * @returns {Object} JSON Content
37 | */
38 | let getConfig = function(){
39 | if (!fs.existsSync(configPath)){
40 | log.error("Config does not exist! Make sure you copy config.template.json and paste it as 'config.json'. Then configure it.");
41 | process.exit(1);
42 | }
43 |
44 | let jsondata = "";
45 | try {
46 | jsondata = String(fs.readFileSync(configPath));
47 | }
48 | catch (e){
49 | log.error(`Cannot read config file: ${e}`);
50 | process.exit(1);
51 | }
52 |
53 | if (validJson(jsondata)) return JSON.parse(jsondata);
54 |
55 | log.error("Config is not valid JSON. Stopping...");
56 | return process.exit(1);
57 | };
58 |
59 | let getVersion = function(){
60 | return packagefile.version;
61 | };
62 |
63 | let getName = function(){
64 | return packagefile.name;
65 | };
66 |
67 | let getAuthor = function(){
68 | return packagefile.author;
69 | };
70 |
71 | let getDescription = function(){
72 | return packagefile.description;
73 | };
74 |
75 | module.exports = {
76 | getConfig,
77 | getVersion,
78 | getName,
79 | getAuthor,
80 | getDescription
81 | };
82 |
--------------------------------------------------------------------------------
/nulldev-template/src/utils/logger.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // ========================= //
4 | // = Copyright (c) NullDev = //
5 | // ========================= //
6 |
7 | /**
8 | * Formats the current time
9 | *
10 | * @returns {String} Time
11 | */
12 | let getDate = function(){
13 | const date = new Date();
14 | let hourData = date.getHours();
15 | let minData = date.getMinutes();
16 | let secData = date.getSeconds();
17 |
18 | let hour = (hourData < 10 ? "0" : "") + hourData;
19 | let min = (minData < 10 ? "0" : "") + minData;
20 | let sec = (secData < 10 ? "0" : "") + secData;
21 |
22 | return "[" + hour + ":" + min + ":" + sec + "]";
23 | };
24 |
25 | /**
26 | * Get the StackTrace of the calee function
27 | *
28 | * @returns {String} StackTrace
29 | */
30 | let getTrace = function(){
31 | let err = new Error();
32 | let splitArr = err.stack.split("\n").filter((_, index) => index > 1);
33 | let cleanArr = "";
34 |
35 | for (let element of splitArr){
36 | if (element.match(/(node_modules)/gi)) break;
37 | cleanArr += cleanArr.length ? " " : "";
38 | cleanArr += element.replace(/( at )/gi, "") + "\n";
39 | }
40 | return cleanArr.substring(0, cleanArr.lastIndexOf("\n")) + cleanArr.substring(cleanArr.lastIndexOf("\n") + 1);
41 | };
42 |
43 | module.exports = {
44 | error(input){
45 | console.log(
46 | " \x1b[41m\x1b[315m x \x1b[0m\x1b[31m [ERROR] " + getDate() + " - " + input + "\n" +
47 | " StackTrace: " + getTrace() + "\x1b[0m"
48 | );
49 | },
50 |
51 | warn(input){
52 | console.log(" \x1b[43m\x1b[30m ! \x1b[0m\x1b[33m [WARN] " + getDate() + " - " + input + "\x1b[0m");
53 | },
54 |
55 | info(input){
56 | console.log(" \x1b[44m\x1b[30m i \x1b[0m\x1b[36m [INFO] " + getDate() + " - " + input + "\x1b[0m");
57 | },
58 |
59 | done(input){
60 | console.log(" \x1b[42m\x1b[30m ✓ \x1b[0m\x1b[32m [DONE] " + getDate() + " - " + input + "\x1b[0m");
61 | }
62 | };
63 |
--------------------------------------------------------------------------------