├── .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 | [![NullDev JavaScript Styleguide](https://i.imgur.com/VcFtkgK.png)](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 | [![NullDev JavaScript Styleguide](https://i.imgur.com/52YayNH.png)](https://nulldev.org) 714 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NullDev JavaScript Styleguide](https://i.imgur.com/VcFtkgK.png)](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 | [![NullDev JavaScript Styleguide](https://i.imgur.com/52YayNH.png)](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 | --------------------------------------------------------------------------------