@entva/styleguide 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.github/workflows/ci.yml +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +88 -0
  4. package/css/README.md +385 -0
  5. package/css/packages/stylelint/LICENSE +21 -0
  6. package/css/packages/stylelint/README.md +27 -0
  7. package/css/packages/stylelint/eslint.config.js +3 -0
  8. package/css/packages/stylelint/index.js +84 -0
  9. package/css/packages/stylelint/package-lock.json +4042 -0
  10. package/css/packages/stylelint/package.json +42 -0
  11. package/html/README.md +98 -0
  12. package/javascript/README.md +3255 -0
  13. package/javascript/packages/eslint-base/LICENSE +21 -0
  14. package/javascript/packages/eslint-base/README.md +27 -0
  15. package/javascript/packages/eslint-base/eslint.config.js +3 -0
  16. package/javascript/packages/eslint-base/index.js +1084 -0
  17. package/javascript/packages/eslint-base/package-lock.json +2653 -0
  18. package/javascript/packages/eslint-base/package.json +36 -0
  19. package/javascript/packages/eslint-react/LICENSE +21 -0
  20. package/javascript/packages/eslint-react/README.md +27 -0
  21. package/javascript/packages/eslint-react/eslint.config.js +3 -0
  22. package/javascript/packages/eslint-react/index.js +526 -0
  23. package/javascript/packages/eslint-react/package-lock.json +3035 -0
  24. package/javascript/packages/eslint-react/package.json +42 -0
  25. package/package.json +15 -0
  26. package/react/README.md +726 -0
  27. package/scripts/for-each-package +12 -0
  28. package/test/css.js +36 -0
  29. package/test/index.js +7 -0
  30. package/test/js.js +35 -0
  31. package/test/utils.js +63 -0
  32. package/tooling/packages/biome/LICENSE +21 -0
  33. package/tooling/packages/biome/README.md +27 -0
  34. package/tooling/packages/biome/biome.json +1285 -0
  35. package/tooling/packages/biome/package-lock.json +183 -0
  36. package/tooling/packages/biome/package.json +38 -0
  37. package/tsconfig.json +31 -0
  38. package/typescript/README.md +66 -0
  39. package/typescript/packages/eslint-typescript-base/LICENSE +21 -0
  40. package/typescript/packages/eslint-typescript-base/README.md +27 -0
  41. package/typescript/packages/eslint-typescript-base/eslint.config.js +3 -0
  42. package/typescript/packages/eslint-typescript-base/index.js +272 -0
  43. package/typescript/packages/eslint-typescript-base/package-lock.json +3215 -0
  44. package/typescript/packages/eslint-typescript-base/package.json +41 -0
  45. package/typescript/packages/eslint-typescript-base/tsconfig.json +30 -0
  46. package/typescript/packages/eslint-typescript-react/LICENSE +21 -0
  47. package/typescript/packages/eslint-typescript-react/README.md +27 -0
  48. package/typescript/packages/eslint-typescript-react/eslint.config.js +3 -0
  49. package/typescript/packages/eslint-typescript-react/index.js +75 -0
  50. package/typescript/packages/eslint-typescript-react/package-lock.json +3630 -0
  51. package/typescript/packages/eslint-typescript-react/package.json +41 -0
  52. package/typescript/packages/eslint-typescript-react/tsconfig.json +30 -0
@@ -0,0 +1,3255 @@
1
+ # JavaScript Style Guide
2
+
3
+ *A mostly reasonable approach to JavaScript*. Read the fine manual here: [http://bonsaiden.github.io/JavaScript-Garden/](http://bonsaiden.github.io/JavaScript-Garden/). Bonus read: [http://eloquentjavascript.net](http://eloquentjavascript.net).
4
+
5
+ Based on [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/blob/master/README.md)
6
+
7
+ > **Note**: this guide assumes you are using [Babel](https://babeljs.io) or similar. It also assumes you are installing shims/polyfills in your app.
8
+
9
+ ## Table of Contents
10
+
11
+ 1. [Types](#types)
12
+ 1. [References](#references)
13
+ 1. [Objects](#objects)
14
+ 1. [Arrays](#arrays)
15
+ 1. [Destructuring](#destructuring)
16
+ 1. [Strings](#strings)
17
+ 1. [Functions](#functions)
18
+ 1. [Arrow Functions](#arrow-functions)
19
+ 1. [Classes & Constructors](#classes--constructors)
20
+ 1. [Modules](#modules)
21
+ 1. [Iterators and Generators](#iterators-and-generators)
22
+ 1. [Properties](#properties)
23
+ 1. [Variables](#variables)
24
+ 1. [Hoisting](#hoisting)
25
+ 1. [Comparison Operators & Equality](#comparison-operators--equality)
26
+ 1. [Blocks](#blocks)
27
+ 1. [Control Statements](#control-statements)
28
+ 1. [Comments](#comments)
29
+ 1. [Whitespace](#whitespace)
30
+ 1. [Commas](#commas)
31
+ 1. [Semicolons](#semicolons)
32
+ 1. [Type Casting & Coercion](#type-casting--coercion)
33
+ 1. [Naming Conventions](#naming-conventions)
34
+ 1. [Accessors](#accessors)
35
+ 1. [Events](#events)
36
+ 1. [jQuery](#jquery)
37
+ 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility)
38
+ 1. [ECMAScript 6+ (ES 2015+) Styles](#ecmascript-6-es-2015-styles)
39
+ 1. [Standard Library](#standard-library)
40
+ 1. [Testing](#testing)
41
+
42
+ ## Types
43
+
44
+ <a name="types--primitives"></a>
45
+ - [1.1](#types--primitives) **Primitives**: When you access a primitive type you work directly on its value.
46
+
47
+ - `string`
48
+ - `number`
49
+ - `boolean`
50
+ - `null`
51
+ - `undefined`
52
+ - `symbol`
53
+ - `bigint`
54
+
55
+ ```javascript
56
+ const foo = 1;
57
+ let bar = foo;
58
+
59
+ bar = 9;
60
+
61
+ console.log(foo, bar); // => 1, 9
62
+ ```
63
+
64
+ - Symbols and BigInts cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don’t support them natively.
65
+
66
+ <a name="types--complex"></a>
67
+ - [1.2](#types--complex) **Complex**: When you access a complex type you work on a reference to its value.
68
+
69
+ - `object`
70
+ - `array`
71
+ - `function`
72
+
73
+ ```javascript
74
+ const foo = [1, 2];
75
+ const bar = foo;
76
+
77
+ bar[0] = 9;
78
+
79
+ console.log(foo[0], bar[0]); // => 9, 9
80
+ ```
81
+
82
+ **[⬆ back to top](#table-of-contents)**
83
+
84
+ ## References
85
+
86
+ <a name="references--prefer-const"></a>
87
+ - [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)
88
+
89
+ > Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
90
+
91
+ ```javascript
92
+ // bad
93
+ var a = 1;
94
+ var b = 2;
95
+
96
+ // good
97
+ const a = 1;
98
+ const b = 2;
99
+ ```
100
+
101
+ <a name="references--disallow-var"></a>
102
+ - [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)
103
+
104
+ > Why? `let` is block-scoped rather than function-scoped like `var`.
105
+
106
+ ```javascript
107
+ // bad
108
+ var count = 1;
109
+ if (true) {
110
+ count += 1;
111
+ }
112
+
113
+ // good, use the let.
114
+ let count = 1;
115
+ if (true) {
116
+ count += 1;
117
+ }
118
+ ```
119
+
120
+ <a name="references--block-scope"></a>
121
+ - [2.3](#references--block-scope) Note that both `let` and `const` are block-scoped.
122
+
123
+ ```javascript
124
+ // const and let only exist in the blocks they are defined in.
125
+ {
126
+ let a = 1;
127
+ const b = 1;
128
+ var c = 1;
129
+ }
130
+ console.log(a); // ReferenceError
131
+ console.log(b); // ReferenceError
132
+ console.log(c); // Prints 1
133
+ ```
134
+
135
+ In the above code, you can see that referencing `a` and `b` will produce a ReferenceError, while `c` contains the number. This is because `a` and `b` are block scoped, while `c` is scoped to the containing function.
136
+
137
+ **[⬆ back to top](#table-of-contents)**
138
+
139
+ ## Objects
140
+
141
+ <a name="objects--no-new"></a>
142
+ - [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)
143
+
144
+ ```javascript
145
+ // bad
146
+ const item = new Object();
147
+
148
+ // good
149
+ const item = {};
150
+ ```
151
+
152
+ <a name="es6-computed-properties"></a>
153
+ - [3.2](#es6-computed-properties) Use computed property names when creating objects with dynamic property names.
154
+
155
+ > Why? They allow you to define all the properties of an object in one place.
156
+
157
+ ```javascript
158
+
159
+ function getKey(k) {
160
+ return `a key named ${k}`;
161
+ }
162
+
163
+ // bad
164
+ const obj = {
165
+ id: 5,
166
+ name: 'San Francisco',
167
+ };
168
+ obj[getKey('enabled')] = true;
169
+
170
+ // good
171
+ const obj = {
172
+ id: 5,
173
+ name: 'San Francisco',
174
+ [getKey('enabled')]: true,
175
+ };
176
+ ```
177
+
178
+ <a name="es6-object-shorthand"></a>
179
+ - [3.3](#es6-object-shorthand) Use object method shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
180
+
181
+ ```javascript
182
+ // bad
183
+ const atom = {
184
+ value: 1,
185
+
186
+ addValue: function (value) {
187
+ return atom.value + value;
188
+ },
189
+ };
190
+
191
+ // good
192
+ const atom = {
193
+ value: 1,
194
+
195
+ addValue(value) {
196
+ return atom.value + value;
197
+ },
198
+ };
199
+ ```
200
+
201
+ <a name="es6-object-concise"></a>
202
+ - [3.4](#es6-object-concise) Use property value shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
203
+
204
+ > Why? It is shorter and descriptive.
205
+
206
+ ```javascript
207
+ const lukeSkywalker = 'Luke Skywalker';
208
+
209
+ // bad
210
+ const obj = {
211
+ lukeSkywalker: lukeSkywalker,
212
+ };
213
+
214
+ // good
215
+ const obj = {
216
+ lukeSkywalker,
217
+ };
218
+ ```
219
+
220
+ <a name="objects--grouped-shorthand"></a>
221
+ - [3.5](#objects--grouped-shorthand) Group your shorthand properties at the beginning of your object declaration.
222
+
223
+ > Why? It’s easier to tell which properties are using the shorthand.
224
+
225
+ ```javascript
226
+ const anakinSkywalker = 'Anakin Skywalker';
227
+ const lukeSkywalker = 'Luke Skywalker';
228
+
229
+ // bad
230
+ const obj = {
231
+ episodeOne: 1,
232
+ twoJediWalkIntoACantina: 2,
233
+ lukeSkywalker,
234
+ episodeThree: 3,
235
+ mayTheFourth: 4,
236
+ anakinSkywalker,
237
+ };
238
+
239
+ // good
240
+ const obj = {
241
+ lukeSkywalker,
242
+ anakinSkywalker,
243
+ episodeOne: 1,
244
+ twoJediWalkIntoACantina: 2,
245
+ episodeThree: 3,
246
+ mayTheFourth: 4,
247
+ };
248
+ ```
249
+
250
+ <a name="objects--quoted-props"></a>
251
+ - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html)
252
+
253
+ > Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
254
+
255
+ ```javascript
256
+ // bad
257
+ const bad = {
258
+ 'foo': 3,
259
+ 'bar': 4,
260
+ 'data-blah': 5,
261
+ };
262
+
263
+ // good
264
+ const good = {
265
+ foo: 3,
266
+ bar: 4,
267
+ 'data-blah': 5,
268
+ };
269
+ ```
270
+
271
+ <a name="objects--prototype-builtins"></a>
272
+ - [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)
273
+
274
+ > 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)`).
275
+
276
+ ```javascript
277
+ // bad
278
+ console.log(object.hasOwnProperty(key));
279
+
280
+ // good
281
+ console.log(Object.prototype.hasOwnProperty.call(object, key));
282
+
283
+ // best
284
+ const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
285
+ // ...
286
+ console.log(has.call(object, key));
287
+ ```
288
+
289
+ <a name="objects--rest-spread"></a>
290
+ - [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.
291
+
292
+ ```javascript
293
+ // very bad
294
+ const original = { a: 1, b: 2 };
295
+ const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
296
+ delete copy.a; // so does this
297
+
298
+ // bad
299
+ const original = { a: 1, b: 2 };
300
+ const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
301
+
302
+ // good
303
+ const original = { a: 1, b: 2 };
304
+ const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
305
+
306
+ const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
307
+ ```
308
+
309
+ **[⬆ back to top](#table-of-contents)**
310
+
311
+ ## Arrays
312
+
313
+ <a name="arrays--literals"></a>
314
+ - [4.1](#arrays--literals) Use the literal syntax for array creation. eslint: [`no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor.html)
315
+
316
+ ```javascript
317
+ // bad
318
+ const items = new Array();
319
+
320
+ // good
321
+ const items = [];
322
+ ```
323
+
324
+ <a name="arrays--push"></a>
325
+ - [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.
326
+
327
+ ```javascript
328
+ const someStack = [];
329
+
330
+ // bad
331
+ someStack[someStack.length] = 'abracadabra';
332
+
333
+ // good
334
+ someStack.push('abracadabra');
335
+ ```
336
+
337
+ <a name="es6-array-spreads"></a>
338
+ - [4.3](#es6-array-spreads) Use array spreads `...` to copy arrays.
339
+
340
+ ```javascript
341
+ // bad
342
+ const len = items.length;
343
+ const itemsCopy = [];
344
+ let i;
345
+
346
+ for (i = 0; i < len; i += 1) {
347
+ itemsCopy[i] = items[i];
348
+ }
349
+
350
+ // good
351
+ const itemsCopy = items.slice();
352
+
353
+ // best
354
+ const anotherCopy = [...items];
355
+ ```
356
+
357
+ <a name="arrays--from"></a>
358
+ <a name="arrays--from-iterable"></a>
359
+ - [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).
360
+
361
+ ```javascript
362
+ const foo = document.querySelectorAll('.foo');
363
+
364
+ // good
365
+ const nodes = Array.from(foo);
366
+
367
+ // best
368
+ const nodes = [...foo];
369
+ ```
370
+
371
+ <a name="arrays--from-array-like"></a>
372
+ - [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.
373
+
374
+ ```javascript
375
+ const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
376
+
377
+ // bad
378
+ const arr = Array.prototype.slice.call(arrLike);
379
+
380
+ // good
381
+ const arr = Array.from(arrLike);
382
+ ```
383
+
384
+ <a name="arrays--mapping"></a>
385
+ - [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. Use this for Map/Set etc.
386
+
387
+ ```javascript
388
+ // bad
389
+ const baz = [...foo].map(bar);
390
+
391
+ // good
392
+ const baz = Array.from(foo, bar);
393
+ ```
394
+
395
+ <a name="arrays--callback-return"></a>
396
+ - [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)
397
+
398
+ ```javascript
399
+ // good
400
+ [1, 2, 3].map((x) => {
401
+ const y = x + 1;
402
+ return x * y;
403
+ });
404
+
405
+ // good
406
+ [1, 2, 3].map((x) => (x + 1));
407
+
408
+ // bad - no returned value means `acc` becomes undefined after the first iteration
409
+ [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
410
+ const flatten = acc.concat(item);
411
+ });
412
+
413
+ // good
414
+ [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
415
+ const flatten = acc.concat(item);
416
+ return flatten;
417
+ });
418
+
419
+ // bad
420
+ inbox.filter((msg) => {
421
+ const { subject, author } = msg;
422
+ if (subject === 'Mockingbird') {
423
+ return author === 'Harper Lee';
424
+ } else {
425
+ return false;
426
+ }
427
+ });
428
+
429
+ // good
430
+ inbox.filter((msg) => {
431
+ const { subject, author } = msg;
432
+ if (subject === 'Mockingbird') {
433
+ return author === 'Harper Lee';
434
+ }
435
+
436
+ return false;
437
+ });
438
+ ```
439
+
440
+ <a name="arrays--bracket-newline"></a>
441
+ - [4.8](#arrays--bracket-newline) Use line breaks after open and before close array brackets if an array has multiple lines
442
+
443
+ ```javascript
444
+ // bad
445
+ const arr = [
446
+ [0, 1], [2, 3], [4, 5],
447
+ ];
448
+
449
+ const objectInArray = [{
450
+ id: 1,
451
+ }, {
452
+ id: 2,
453
+ }];
454
+
455
+ const numberInArray = [
456
+ 1, 2,
457
+ ];
458
+
459
+ // good
460
+ const arr = [[0, 1], [2, 3], [4, 5]];
461
+
462
+ const objectInArray = [
463
+ { id: 1 },
464
+ {
465
+ id: 2,
466
+ anotherPropThatIsTooLong: 3,
467
+ },
468
+ ];
469
+
470
+ const numberInArray = [
471
+ 1,
472
+ 2,
473
+ ];
474
+ ```
475
+
476
+ **[⬆ back to top](#table-of-contents)**
477
+
478
+ ## Destructuring
479
+
480
+ <a name="destructuring--object"></a>
481
+ - [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)
482
+
483
+ > 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.
484
+
485
+ ```javascript
486
+ // bad
487
+ function getFullName(user) {
488
+ const firstName = user.firstName;
489
+ const lastName = user.lastName;
490
+
491
+ return `${firstName} ${lastName}`;
492
+ }
493
+
494
+ // good
495
+ function getFullName(user) {
496
+ const { firstName, lastName } = user;
497
+ return `${firstName} ${lastName}`;
498
+ }
499
+
500
+ // best
501
+ function getFullName({ firstName, lastName }) {
502
+ return `${firstName} ${lastName}`;
503
+ }
504
+ ```
505
+
506
+ <a name="destructuring--array"></a>
507
+ - [5.2](#destructuring--array) Use array destructuring. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
508
+
509
+ ```javascript
510
+ const arr = [1, 2, 3, 4];
511
+
512
+ // bad
513
+ const first = arr[0];
514
+ const second = arr[1];
515
+
516
+ // good
517
+ const [first, second] = arr;
518
+ ```
519
+
520
+ <a name="destructuring--object-over-array"></a>
521
+ - [5.3](#destructuring--object-over-array) Use object destructuring for multiple return values, not array destructuring.
522
+
523
+ > Why? You can add new properties over time or change the order of things without breaking call sites.
524
+
525
+ ```javascript
526
+ // bad
527
+ function processInput(input) {
528
+ // then a miracle occurs
529
+ return [left, right, top, bottom];
530
+ }
531
+
532
+ // the caller needs to think about the order of return data
533
+ const [left, _, top] = processInput(input);
534
+
535
+ // good
536
+ function processInput(input) {
537
+ // then a miracle occurs
538
+ return { left, right, top, bottom };
539
+ }
540
+
541
+ // the caller selects only the data they need
542
+ const { left, top } = processInput(input);
543
+ ```
544
+
545
+ **[⬆ back to top](#table-of-contents)**
546
+
547
+ ## Strings
548
+
549
+ <a name="strings--quotes"></a>
550
+ - [6.1](#strings--quotes) Use single quotes `''` for strings. eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html)
551
+
552
+ ```javascript
553
+ // bad
554
+ const name = "Capt. Janeway";
555
+
556
+ // bad - template literals should contain interpolation or newlines
557
+ const name = `Capt. Janeway`;
558
+
559
+ // good
560
+ const name = 'Capt. Janeway';
561
+ ```
562
+
563
+ <a name="strings--line-length"></a>
564
+ - [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.
565
+
566
+ > Why? Broken strings are painful to work with and make code less searchable.
567
+
568
+ ```javascript
569
+ // bad
570
+ const errorMessage = 'This is a super long error that was thrown because \
571
+ of Batman. When you stop to think about how Batman had anything to do \
572
+ with this, you would get nowhere \
573
+ fast.';
574
+
575
+ // bad
576
+ const errorMessage = 'This is a super long error that was thrown because ' +
577
+ 'of Batman. When you stop to think about how Batman had anything to do ' +
578
+ 'with this, you would get nowhere fast.';
579
+
580
+ // good
581
+ const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
582
+ ```
583
+
584
+ <a name="es6-template-literals"></a>
585
+ - [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)
586
+
587
+ > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
588
+
589
+ ```javascript
590
+ // bad
591
+ function sayHi(name) {
592
+ return 'How are you, ' + name + '?';
593
+ }
594
+
595
+ // bad
596
+ function sayHi(name) {
597
+ return ['How are you, ', name, '?'].join();
598
+ }
599
+
600
+ // bad
601
+ function sayHi(name) {
602
+ return `How are you, ${ name }?`;
603
+ }
604
+
605
+ // good
606
+ function sayHi(name) {
607
+ return `How are you, ${name}?`;
608
+ }
609
+ ```
610
+
611
+ <a name="strings--eval"></a>
612
+ - [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)
613
+
614
+ <a name="strings--escaping"></a>
615
+ - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings. eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape)
616
+
617
+ > Why? Backslashes harm readability, thus they should only be present when necessary.
618
+
619
+ ```javascript
620
+ // bad
621
+ const foo = '\'this\' \i\s \"quoted\"';
622
+
623
+ // good
624
+ const foo = '\'this\' is "quoted"';
625
+ const foo = `my name is '${name}'`;
626
+ ```
627
+
628
+ **[⬆ back to top](#table-of-contents)**
629
+
630
+ ## Functions
631
+
632
+ <a name="functions--declarations"></a>
633
+ - [7.1](#functions--declarations) Prefer anonymous arrow functions assigned to variables. eslint: [`func-style`](https://eslint.org/docs/rules/func-style)
634
+
635
+ > Why? It's shorter and more readable. The name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel) and the stack traces contain the character and line in any case.
636
+
637
+ ```javascript
638
+ // bad
639
+ function foo() {
640
+ // ...
641
+ }
642
+
643
+ // good
644
+ // lexical name distinguished from the variable-referenced invocation(s)
645
+ const short = function longUniqueMoreDescriptiveLexicalFoo() {
646
+ // ...
647
+ };
648
+
649
+ // good
650
+ const foo = function () {
651
+ // ...
652
+ };
653
+
654
+ // best
655
+ const foo = () => {
656
+ // ...
657
+ };
658
+ ```
659
+
660
+ <a name="functions--iife"></a>
661
+ - [7.2](#functions--iife) Wrap immediately invoked function expressions in parentheses. eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife.html)
662
+
663
+ > 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.
664
+
665
+ ```javascript
666
+ // immediately-invoked function expression (IIFE)
667
+ (function () {
668
+ console.log('Welcome to the Internet. Please follow me.');
669
+ }());
670
+ ```
671
+
672
+ <a name="functions--in-blocks"></a>
673
+ - [7.3](#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)
674
+
675
+ <a name="functions--note-on-blocks"></a>
676
+ - [7.4](#functions--note-on-blocks) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement.
677
+
678
+ ```javascript
679
+ // bad
680
+ if (currentUser) {
681
+ function test() {
682
+ console.log('Nope.');
683
+ }
684
+ }
685
+
686
+ // good
687
+ let test;
688
+ if (currentUser) {
689
+ test = () => {
690
+ console.log('Yup.');
691
+ };
692
+ }
693
+ ```
694
+
695
+ <a name="functions--arguments-shadow"></a>
696
+ - [7.5](#functions--arguments-shadow) Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope.
697
+
698
+ ```javascript
699
+ // bad
700
+ function (name, options, arguments) {
701
+ // ...
702
+ }
703
+
704
+ // good
705
+ function (name, options, args) {
706
+ // ...
707
+ }
708
+ ```
709
+
710
+ <a name="es6-rest"></a>
711
+ - [7.6](#es6-rest) Never use `arguments`, opt to use rest syntax `...` instead. eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params)
712
+
713
+ > Why? `...` is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like `arguments`.
714
+
715
+ ```javascript
716
+ // bad
717
+ function () {
718
+ const args = Array.prototype.slice.call(arguments);
719
+ return args.join('');
720
+ }
721
+
722
+ // good
723
+ function (...args) {
724
+ return args.join('');
725
+ }
726
+
727
+ // best
728
+ (...args) => args.join('');
729
+ ```
730
+
731
+ <a name="es6-default-parameters"></a>
732
+ - [7.7](#es6-default-parameters) Use default parameter syntax rather than mutating function arguments.
733
+
734
+ ```javascript
735
+ // really bad
736
+ function (opts) {
737
+ // No! We shouldn’t mutate function arguments.
738
+ // Double bad: if opts is falsy it'll be set to an object which may
739
+ // be what you want but it can introduce subtle bugs.
740
+ opts = opts || {};
741
+ // ...
742
+ }
743
+
744
+ // still bad
745
+ function (opts) {
746
+ if (opts === void 0) {
747
+ opts = {};
748
+ }
749
+ // ...
750
+ }
751
+
752
+ // good
753
+ function (opts = {}) {
754
+ // ...
755
+ }
756
+ ```
757
+
758
+ <a name="functions--default-side-effects"></a>
759
+ - [7.8](#functions--default-side-effects) Avoid side effects with default parameters.
760
+
761
+ > Why? They are confusing to reason about.
762
+
763
+ ```javascript
764
+ var b = 1;
765
+ // bad
766
+ function (a = b++) {
767
+ console.log(a);
768
+ }
769
+ count(); // 1
770
+ count(); // 2
771
+ count(3); // 3
772
+ count(); // 3
773
+ ```
774
+
775
+ <a name="functions--defaults-last"></a>
776
+ - [7.9](#functions--defaults-last) Always put default parameters last.
777
+
778
+ ```javascript
779
+ // bad
780
+ function (opts = {}, name) {
781
+ // ...
782
+ }
783
+
784
+ // good
785
+ function (name, opts = {}) {
786
+ // ...
787
+ }
788
+ ```
789
+
790
+ <a name="functions--constructor"></a>
791
+ - [7.10](#functions--constructor) Never use the Function constructor to create a new function. eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func)
792
+
793
+ > Why? Creating a function in this way evaluates a string similarly to `eval()`, which opens vulnerabilities.
794
+
795
+ ```javascript
796
+ // bad
797
+ var add = new Function('a', 'b', 'return a + b');
798
+
799
+ // still bad
800
+ var subtract = Function('a', 'b', 'return a - b');
801
+ ```
802
+
803
+ <a name="functions--signature-spacing"></a>
804
+ - [7.11](#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)
805
+
806
+ > Why? It is similar to defining functions within an object syntax. Consistency is good.
807
+
808
+ ```javascript
809
+ // bad
810
+ const f = function a(){};
811
+ const g = function (){};
812
+
813
+ // ok
814
+ const h = function() {};
815
+
816
+ // good
817
+ const y = function a() {};
818
+ const x = function () {};
819
+ ```
820
+
821
+ <a name="functions--mutate-params"></a>
822
+ - [7.12](#functions--mutate-params) Try not to mutate parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
823
+
824
+ > Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.
825
+
826
+ ```javascript
827
+ // bad
828
+ function (obj) {
829
+ obj.key = 1;
830
+ }
831
+ ```
832
+
833
+ <a name="functions--reassign-params"></a>
834
+ - [7.13](#functions--reassign-params) Never reassign parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
835
+
836
+ > Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the `arguments` object. It can also cause optimization issues, especially in V8.
837
+
838
+ ```javascript
839
+ // bad
840
+ function (a) {
841
+ a = 1;
842
+ // ...
843
+ }
844
+
845
+ function (a) {
846
+ if (!a) { a = 1; }
847
+ // ...
848
+ }
849
+
850
+ // good
851
+ function (a) {
852
+ const b = a || 1;
853
+ // ...
854
+ }
855
+
856
+ function (a = 1) {
857
+ // ...
858
+ }
859
+ ```
860
+
861
+ <a name="functions--spread-vs-apply"></a>
862
+ - [7.14](#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)
863
+
864
+ > Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose `new` with `apply`.
865
+
866
+ ```javascript
867
+ // bad
868
+ const x = [1, 2, 3, 4, 5];
869
+ console.log.apply(console, x);
870
+
871
+ // good
872
+ const x = [1, 2, 3, 4, 5];
873
+ console.log(...x);
874
+
875
+ // bad
876
+ new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
877
+
878
+ // good
879
+ new Date(...[2016, 8, 5]);
880
+ ```
881
+
882
+ <a name="functions--signature-invocation-indentation"></a>
883
+ - [7.15](#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, with a trailing comma on the last item. eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline)
884
+
885
+ ```javascript
886
+ // bad
887
+ function (bar,
888
+ baz,
889
+ quux) {
890
+ // ...
891
+ }
892
+
893
+ // good
894
+ function (
895
+ bar,
896
+ baz,
897
+ quux,
898
+ ) {
899
+ // ...
900
+ }
901
+
902
+ // bad
903
+ console.log(foo,
904
+ bar,
905
+ baz);
906
+
907
+ // good
908
+ console.log(
909
+ foo,
910
+ bar,
911
+ baz,
912
+ );
913
+ ```
914
+
915
+ **[⬆ back to top](#table-of-contents)**
916
+
917
+ ## Arrow Functions
918
+
919
+ <a name="arrows--use-them"></a>
920
+ - [8.1](#arrows--use-them) Use arrow function notation whenever possible. eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing.html)
921
+
922
+ > 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.
923
+
924
+ > Why not? If you have a fairly complicated function, you might move that logic out into its own function.
925
+
926
+ ```javascript
927
+ // bad
928
+ [1, 2, 3].map(function (x) {
929
+ const y = x + 1;
930
+ return x * y;
931
+ });
932
+
933
+ // good
934
+ [1, 2, 3].map((x) => {
935
+ const y = x + 1;
936
+ return x * y;
937
+ });
938
+ ```
939
+
940
+ <a name="arrows--implicit-return"></a>
941
+ - [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)
942
+
943
+ > Why? Syntactic sugar. It reads well when multiple functions are chained together.
944
+
945
+ ```javascript
946
+ // bad
947
+ [1, 2, 3].map((number) => {
948
+ const nextNumber = number + 1;
949
+ `A string containing the ${nextNumber}.`;
950
+ });
951
+
952
+ // good
953
+ [1, 2, 3].map((number) => `A string containing the ${number + 1}.`);
954
+
955
+ // good
956
+ [1, 2, 3].map((number) => {
957
+ const nextNumber = number + 1;
958
+ return `A string containing the ${nextNumber}.`;
959
+ });
960
+
961
+ // good
962
+ [1, 2, 3].map((number, index) => ({
963
+ [index]: number,
964
+ }));
965
+
966
+ // No implicit return with side effects
967
+ const foo = function (callback) {
968
+ const val = callback();
969
+ if (val === true) {
970
+ // Do something if callback returns true
971
+ }
972
+ }
973
+
974
+ let bool = false;
975
+
976
+ // bad
977
+ foo(() => bool = true);
978
+
979
+ // good
980
+ foo(() => {
981
+ bool = true;
982
+ });
983
+ ```
984
+
985
+ <a name="arrows--paren-wrap"></a>
986
+ - [8.3](#arrows--paren-wrap) In case the expression spans over multiple lines, wrap it in parentheses for better readability.
987
+
988
+ > Why? It shows clearly where the function starts and ends.
989
+
990
+ ```javascript
991
+ // bad
992
+ ['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
993
+ httpMagicObjectWithAVeryLongName,
994
+ httpMethod,
995
+ )
996
+ );
997
+
998
+ // good
999
+ ['get', 'post', 'put'].map((httpMethod) => (
1000
+ Object.prototype.hasOwnProperty.call(
1001
+ httpMagicObjectWithAVeryLongName,
1002
+ httpMethod,
1003
+ )
1004
+ ));
1005
+ ```
1006
+
1007
+ <a name="arrows--one-arg-parens"></a>
1008
+ - [8.4](#arrows--one-arg-parens) Always include parentheses around arguments for clarity and consistency. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html)
1009
+
1010
+ > Why? Consistency.
1011
+
1012
+ ```javascript
1013
+ // bad
1014
+ [1, 2, 3].map(x => x * x);
1015
+
1016
+ // good
1017
+ [1, 2, 3].map((x) => x * x);
1018
+
1019
+ // good
1020
+ [1, 2, 3].map((number) => (
1021
+ `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
1022
+ ));
1023
+
1024
+ // bad
1025
+ [1, 2, 3].map(x => {
1026
+ const y = x + 1;
1027
+ return x * y;
1028
+ });
1029
+
1030
+ // good
1031
+ [1, 2, 3].map((x) => {
1032
+ const y = x + 1;
1033
+ return x * y;
1034
+ });
1035
+ ```
1036
+
1037
+ <a name="arrows--confusing"></a>
1038
+ - [8.5](#arrows--confusing) Avoid confusing arrow function syntax (`=>`) with comparison operators (`<=`, `>=`). eslint: [`no-confusing-arrow`](https://eslint.org/docs/rules/no-confusing-arrow)
1039
+
1040
+ ```javascript
1041
+ // bad
1042
+ const itemHeight = item => item.height <= 256 ? item.largeSize : item.smallSize;
1043
+
1044
+ // bad
1045
+ const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
1046
+
1047
+ // good
1048
+ const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
1049
+
1050
+ // good
1051
+ const itemHeight = (item) => {
1052
+ const { height, largeSize, smallSize } = item;
1053
+ return height <= 256 ? largeSize : smallSize;
1054
+ };
1055
+ ```
1056
+
1057
+ <a name="whitespace--implicit-arrow-linebreak"></a>
1058
+ - [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)
1059
+
1060
+ ```javascript
1061
+ // bad
1062
+ foo =>
1063
+ bar;
1064
+
1065
+ foo =>
1066
+ (bar);
1067
+
1068
+ // good
1069
+ (foo) => bar;
1070
+ (foo) => (bar);
1071
+ (foo) => (
1072
+ bar
1073
+ )
1074
+ ```
1075
+
1076
+ **[⬆ back to top](#table-of-contents)**
1077
+
1078
+ ## Classes & Constructors
1079
+
1080
+ <a name="constructors--use-class"></a>
1081
+ - [9.1](#constructors--use-class) Always use `class`. Avoid manipulating `prototype` directly.
1082
+
1083
+ > Why? `class` syntax is more concise and easier to reason about.
1084
+
1085
+ ```javascript
1086
+ // bad
1087
+ function Queue(contents = []) {
1088
+ this.queue = [...contents];
1089
+ }
1090
+ Queue.prototype.pop = function () {
1091
+ const value = this.queue[0];
1092
+ this.queue.splice(0, 1);
1093
+ return value;
1094
+ };
1095
+
1096
+ // good
1097
+ class Queue {
1098
+ constructor(contents = []) {
1099
+ this.queue = [...contents];
1100
+ }
1101
+ pop() {
1102
+ const value = this.queue[0];
1103
+ this.queue.splice(0, 1);
1104
+ return value;
1105
+ }
1106
+ }
1107
+ ```
1108
+
1109
+ <a name="constructors--extends"></a>
1110
+ - [9.2](#constructors--extends) Use `extends` for inheritance.
1111
+
1112
+ > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`.
1113
+
1114
+ ```javascript
1115
+ // bad
1116
+ const inherits = require('inherits');
1117
+ function PeekableQueue(contents) {
1118
+ Queue.apply(this, contents);
1119
+ }
1120
+ inherits(PeekableQueue, Queue);
1121
+ PeekableQueue.prototype.peek = function () {
1122
+ return this.queue[0];
1123
+ };
1124
+
1125
+ // good
1126
+ class PeekableQueue extends Queue {
1127
+ peek() {
1128
+ return this.queue[0];
1129
+ }
1130
+ }
1131
+ ```
1132
+
1133
+ <a name="constructors--chaining"></a>
1134
+ - [9.3](#constructors--chaining) Methods can return `this` to help with method chaining.
1135
+
1136
+ ```javascript
1137
+ // bad
1138
+ Jedi.prototype.jump = function () {
1139
+ this.jumping = true;
1140
+ return true;
1141
+ };
1142
+
1143
+ Jedi.prototype.setHeight = function (height) {
1144
+ this.height = height;
1145
+ };
1146
+
1147
+ const luke = new Jedi();
1148
+ luke.jump(); // => true
1149
+ luke.setHeight(20); // => undefined
1150
+
1151
+ // good
1152
+ class Jedi {
1153
+ jump() {
1154
+ this.jumping = true;
1155
+ return this;
1156
+ }
1157
+
1158
+ setHeight(height) {
1159
+ this.height = height;
1160
+ return this;
1161
+ }
1162
+ }
1163
+
1164
+ const luke = new Jedi();
1165
+
1166
+ luke.jump()
1167
+ .setHeight(20);
1168
+ ```
1169
+
1170
+ <a name="constructors--tostring"></a>
1171
+ - [9.4](#constructors--tostring) It’s okay to write a custom `toXXX()` method, just make sure it works successfully and causes no side effects.
1172
+
1173
+ ```javascript
1174
+ class Jedi {
1175
+ constructor(options = {}) {
1176
+ this.name = options.name || 'no name';
1177
+ }
1178
+
1179
+ getName() {
1180
+ return this.name;
1181
+ }
1182
+
1183
+ toString() {
1184
+ return `Jedi - ${this.getName()}`;
1185
+ }
1186
+ }
1187
+ ```
1188
+
1189
+ <a name="constructors--no-useless"></a>
1190
+ - [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)
1191
+
1192
+ ```javascript
1193
+ // bad
1194
+ class Jedi {
1195
+ constructor() {}
1196
+
1197
+ getName() {
1198
+ return this.name;
1199
+ }
1200
+ }
1201
+
1202
+ // bad
1203
+ class Rey extends Jedi {
1204
+ constructor(...args) {
1205
+ super(...args);
1206
+ }
1207
+ }
1208
+
1209
+ // good
1210
+ class Rey extends Jedi {
1211
+ constructor(...args) {
1212
+ super(...args);
1213
+ this.name = 'Rey';
1214
+ }
1215
+ }
1216
+ ```
1217
+
1218
+ <a name="classes--no-duplicate-members"></a>
1219
+ - [9.6](#classes--no-duplicate-members) Avoid duplicate class members. eslint: [`no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members)
1220
+
1221
+ > Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug.
1222
+
1223
+ ```javascript
1224
+ // bad
1225
+ class Foo {
1226
+ bar() { return 1; }
1227
+ bar() { return 2; }
1228
+ }
1229
+ ```
1230
+
1231
+ **[⬆ back to top](#table-of-contents)**
1232
+
1233
+ ## Modules
1234
+
1235
+ <a name="modules--use-them"></a>
1236
+ - [10.1](#modules--use-them) Prefer modules (`import`/`export`) over a non-standard module system.
1237
+
1238
+ ```javascript
1239
+ // ok
1240
+ const StyleGuide = require('./StyleGuide');
1241
+ module.exports = StyleGuide.es6;
1242
+
1243
+ // better
1244
+ import StyleGuide from './StyleGuide';
1245
+ export default StyleGuide.es6;
1246
+
1247
+ // best
1248
+ import { es6 } from './StyleGuide';
1249
+ export default es6;
1250
+ ```
1251
+
1252
+ <a name="modules--no-wildcard"></a>
1253
+ - [10.2](#modules--no-wildcard) Do not use wildcard imports.
1254
+
1255
+ > Why? This makes sure you have a single default export.
1256
+
1257
+ ```javascript
1258
+ // bad
1259
+ import * as StyleGuide from './StyleGuide';
1260
+
1261
+ // good
1262
+ import StyleGuide from './StyleGuide';
1263
+ ```
1264
+
1265
+ <a name="modules--no-duplicate-imports"></a>
1266
+ - [10.3](#modules--no-duplicate-imports) Only import from a path in one place.
1267
+ eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports)
1268
+ > Why? Having multiple lines that import from the same path can make code harder to maintain.
1269
+
1270
+ ```javascript
1271
+ // bad
1272
+ import foo from 'foo';
1273
+ // … some other imports … //
1274
+ import { named1, named2 } from 'foo';
1275
+
1276
+ // good
1277
+ import foo, { named1, named2 } from 'foo';
1278
+
1279
+ // good
1280
+ import foo, {
1281
+ named1,
1282
+ named2,
1283
+ } from 'foo';
1284
+ ```
1285
+
1286
+ <a name="modules--no-mutable-exports"></a>
1287
+ - [10.4](#modules--no-mutable-exports) Do not export mutable bindings.
1288
+ eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md)
1289
+ > 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.
1290
+
1291
+ ```javascript
1292
+ // bad
1293
+ let foo = 3;
1294
+ export { foo };
1295
+
1296
+ // good
1297
+ const foo = 3;
1298
+ export { foo };
1299
+ ```
1300
+
1301
+ <a name="modules--prefer-default-export"></a>
1302
+ - [10.5](#modules--prefer-default-export) In modules with a single export, prefer default export over named export.
1303
+ eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md)
1304
+ > Why? To encourage more files that only ever export one thing, which is better for readability and maintainability.
1305
+
1306
+ ```javascript
1307
+ // bad
1308
+ export function () {}
1309
+
1310
+ // good
1311
+ export default function () {}
1312
+ ```
1313
+
1314
+ <a name="modules--imports-first"></a>
1315
+ - [10.6](#modules--imports-first) Put all `import`s above non-import statements.
1316
+ eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md)
1317
+ > Why? Since `import`s are hoisted, keeping them all at the top prevents surprising behavior.
1318
+
1319
+ ```javascript
1320
+ // bad
1321
+ import foo from 'foo';
1322
+ foo.init();
1323
+
1324
+ import bar from 'bar';
1325
+
1326
+ // good
1327
+ import foo from 'foo';
1328
+ import bar from 'bar';
1329
+
1330
+ foo.init();
1331
+ ```
1332
+
1333
+ <a name="modules--multiline-imports-over-newlines"></a>
1334
+ - [10.7](#modules--multiline-imports-over-newlines) Multiline imports should be indented just like multiline array and object literals.
1335
+
1336
+ > Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide, as do the trailing commas.
1337
+
1338
+ ```javascript
1339
+ // bad
1340
+ import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
1341
+
1342
+ // good
1343
+ import {
1344
+ longNameA,
1345
+ longNameB,
1346
+ longNameC,
1347
+ longNameD,
1348
+ longNameE,
1349
+ } from 'path';
1350
+ ```
1351
+
1352
+ <a name="modules--no-webpack-loader-syntax"></a>
1353
+ - [10.8](#modules--no-webpack-loader-syntax) Disallow Webpack loader syntax in module import statements.
1354
+ eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md)
1355
+ > Why? Since using Webpack syntax in the imports couples the code to a module bundler. Prefer using the loader syntax in `webpack.config.js`.
1356
+
1357
+ ```javascript
1358
+ // bad
1359
+ import fooSass from 'css!sass!foo.scss';
1360
+ import barCss from 'style!css!bar.css';
1361
+
1362
+ // good
1363
+ import fooSass from 'foo.scss';
1364
+ import barCss from 'bar.css';
1365
+ ```
1366
+
1367
+ <a name="modules--absolute-imports"></a>
1368
+ - [10.9](#modules--absolute-imports) **(Webpack) Recommendation**: Prevent long relative paths in import by using absolute paths.
1369
+ > Why? Relative imports traversing multiple parent directories can get unreadable.
1370
+
1371
+ ```javascript
1372
+ // /client/components/feed_list/index.js
1373
+
1374
+ // bad
1375
+ import State from '../../containers/feed/state';
1376
+
1377
+ // good
1378
+ import State from '@/client/containers/feed/state';
1379
+ ```
1380
+
1381
+ ```javascript
1382
+ // /client/components/feed_list/sub/component.js
1383
+
1384
+ // bad
1385
+ import utils from '@/client/components/feed_list/utils';
1386
+
1387
+ // good
1388
+ import utils from '../utils';
1389
+ ```
1390
+
1391
+ **[⬆ back to top](#table-of-contents)**
1392
+
1393
+ ## Iterators and Generators
1394
+
1395
+ <a name="iterators--nope"></a>
1396
+ - [11.1](#iterators--nope) 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)
1397
+
1398
+ > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.
1399
+
1400
+ > 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.
1401
+
1402
+ ```javascript
1403
+ const numbers = [1, 2, 3, 4, 5];
1404
+
1405
+ // bad
1406
+ let sum = 0;
1407
+ for (let num of numbers) {
1408
+ sum += num;
1409
+ }
1410
+ sum === 15;
1411
+
1412
+ // good
1413
+ let sum = 0;
1414
+ numbers.forEach((num) => {
1415
+ sum += num;
1416
+ });
1417
+ sum === 15;
1418
+
1419
+ // best (use the functional force)
1420
+ const sum = numbers.reduce((total, num) => (total + num), 0);
1421
+ sum === 15;
1422
+
1423
+ // bad
1424
+ const increasedByOne = [];
1425
+ for (let i = 0; i < numbers.length; i++) {
1426
+ increasedByOne.push(numbers[i] + 1);
1427
+ }
1428
+
1429
+ // good
1430
+ const increasedByOne = [];
1431
+ numbers.forEach((num) => {
1432
+ increasedByOne.push(num + 1);
1433
+ });
1434
+
1435
+ // best (keeping it functional)
1436
+ const increasedByOne = numbers.map((num) => num + 1);
1437
+ ```
1438
+
1439
+ <a name="generators--nope"></a>
1440
+ - [11.2](#generators--nope) Don’t use generators for now.
1441
+
1442
+ > Why? They don’t transpile well to ES5.
1443
+
1444
+ <a name="generators--spacing"></a>
1445
+ - [11.3](#generators--spacing) If you must use generators, or if you disregard [our advice](#generators--nope), make sure their function signature is spaced properly. eslint: [`generator-star-spacing`](https://eslint.org/docs/rules/generator-star-spacing)
1446
+
1447
+ > Why? `function` and `*` are part of the same conceptual keyword - `*` is not a modifier for `function`, `function*` is a unique construct, different from `function`.
1448
+
1449
+ ```javascript
1450
+ // bad
1451
+ function * foo() {
1452
+ // ...
1453
+ }
1454
+
1455
+ // bad
1456
+ const bar = function * () {
1457
+ // ...
1458
+ };
1459
+
1460
+ // bad
1461
+ const baz = function *() {
1462
+ // ...
1463
+ };
1464
+
1465
+ // bad
1466
+ const quux = function*() {
1467
+ // ...
1468
+ };
1469
+
1470
+ // bad
1471
+ function*foo() {
1472
+ // ...
1473
+ }
1474
+
1475
+ // bad
1476
+ function *foo() {
1477
+ // ...
1478
+ }
1479
+
1480
+ // very bad
1481
+ function
1482
+ *
1483
+ foo() {
1484
+ // ...
1485
+ }
1486
+
1487
+ // very bad
1488
+ const wat = function
1489
+ *
1490
+ () {
1491
+ // ...
1492
+ };
1493
+
1494
+ // good
1495
+ function* foo() {
1496
+ // ...
1497
+ }
1498
+
1499
+ // good
1500
+ const foo = function* () {
1501
+ // ...
1502
+ };
1503
+ ```
1504
+
1505
+ **[⬆ back to top](#table-of-contents)**
1506
+
1507
+ ## Properties
1508
+
1509
+ <a name="properties--dot"></a>
1510
+ - [12.1](#properties--dot) Use dot notation when accessing properties. eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation.html)
1511
+
1512
+ ```javascript
1513
+ const luke = {
1514
+ jedi: true,
1515
+ age: 28,
1516
+ };
1517
+
1518
+ // bad
1519
+ const isJedi = luke['jedi'];
1520
+
1521
+ // good
1522
+ const isJedi = luke.jedi;
1523
+ ```
1524
+
1525
+ <a name="properties--bracket"></a>
1526
+ - [12.2](#properties--bracket) Use bracket notation `[]` when accessing properties with a variable.
1527
+
1528
+ ```javascript
1529
+ const luke = {
1530
+ jedi: true,
1531
+ age: 28,
1532
+ };
1533
+
1534
+ const getProp = function (prop) {
1535
+ return luke[prop];
1536
+ }
1537
+
1538
+ const isJedi = getProp('jedi');
1539
+ ```
1540
+ <a name="es2016-properties--exponentiation-operator"></a>
1541
+ - [12.3](#es2016-properties--exponentiation-operator) Use exponentiation operator `**` when calculating exponentiations. eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties).
1542
+
1543
+ ```javascript
1544
+ // bad
1545
+ const binary = Math.pow(2, 10);
1546
+
1547
+ // good
1548
+ const binary = 2 ** 10;
1549
+ ```
1550
+
1551
+ **[⬆ back to top](#table-of-contents)**
1552
+
1553
+ ## Variables
1554
+
1555
+ <a name="variables--const"></a>
1556
+ - [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. eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const)
1557
+
1558
+ ```javascript
1559
+ // bad
1560
+ superPower = new SuperPower();
1561
+
1562
+ // good
1563
+ const superPower = new SuperPower();
1564
+ ```
1565
+
1566
+ <a name="variables--one-const"></a>
1567
+ - [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)
1568
+
1569
+ > 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.
1570
+
1571
+ ```javascript
1572
+ // bad
1573
+ const items = getItems(),
1574
+ goSportsTeam = true,
1575
+ dragonball = 'z';
1576
+
1577
+ // bad
1578
+ // (compare to above, and try to spot the mistake)
1579
+ const items = getItems(),
1580
+ goSportsTeam = true;
1581
+ dragonball = 'z';
1582
+
1583
+ // good
1584
+ const items = getItems();
1585
+ const goSportsTeam = true;
1586
+ const dragonball = 'z';
1587
+ ```
1588
+
1589
+ <a name="variables--const-let-group"></a>
1590
+ - [13.3](#variables--const-let-group) Group all your `const`s and then group all your `let`s.
1591
+
1592
+ > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.
1593
+
1594
+ ```javascript
1595
+ // bad
1596
+ let i, len, dragonball,
1597
+ items = getItems(),
1598
+ goSportsTeam = true;
1599
+
1600
+ // bad
1601
+ let i;
1602
+ const items = getItems();
1603
+ let dragonball;
1604
+ const goSportsTeam = true;
1605
+ let len;
1606
+
1607
+ // good
1608
+ const goSportsTeam = true;
1609
+ const items = getItems();
1610
+ let dragonball;
1611
+ let i;
1612
+ let length;
1613
+ ```
1614
+
1615
+ <a name="variables--define-where-used"></a>
1616
+ - [13.4](#variables--define-where-used) Assign variables where you need them, but place them in a reasonable place.
1617
+
1618
+ > Why? `let` and `const` are block scoped and not function scoped.
1619
+
1620
+ ```javascript
1621
+ // bad - unnecessary function call
1622
+ const checkName = function (hasName) {
1623
+ const name = getName();
1624
+
1625
+ if (hasName === 'test') {
1626
+ return false;
1627
+ }
1628
+
1629
+ if (name === 'test') {
1630
+ this.setName('');
1631
+ return false;
1632
+ }
1633
+
1634
+ return name;
1635
+ }
1636
+
1637
+ // good
1638
+ const checkName = function (hasName) {
1639
+ if (hasName === 'test') {
1640
+ return false;
1641
+ }
1642
+
1643
+ const name = getName();
1644
+
1645
+ if (name === 'test') {
1646
+ this.setName('');
1647
+ return false;
1648
+ }
1649
+
1650
+ return name;
1651
+ }
1652
+ ```
1653
+ <a name="variables--no-chain-assignment"></a>
1654
+ - [13.5](#variables--no-chain-assignment) Don’t chain variable assignments. eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign)
1655
+
1656
+ > Why? Chaining variable assignments creates implicit global variables.
1657
+
1658
+ ```javascript
1659
+ // bad
1660
+ (function example() {
1661
+ // JavaScript interprets this as
1662
+ // let a = ( b = ( c = 1 ) );
1663
+ // The let keyword only applies to variable a; variables b and c become
1664
+ // global variables.
1665
+ let a = b = c = 1;
1666
+ }());
1667
+
1668
+ console.log(a); // throws ReferenceError
1669
+ console.log(b); // 1
1670
+ console.log(c); // 1
1671
+
1672
+ // good
1673
+ (function example() {
1674
+ let a = 1;
1675
+ let b = a;
1676
+ let c = a;
1677
+ }());
1678
+
1679
+ console.log(a); // throws ReferenceError
1680
+ console.log(b); // throws ReferenceError
1681
+ console.log(c); // throws ReferenceError
1682
+
1683
+ // the same applies for `const`
1684
+ ```
1685
+
1686
+ <a name="variables--unary-increment-decrement"></a>
1687
+ - [13.6](#variables--unary-increment-decrement) Avoid using unary increments and decrements (`++`, `--`). eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus)
1688
+
1689
+ > 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.
1690
+
1691
+ ```javascript
1692
+ // bad
1693
+ const array = [1, 2, 3];
1694
+ let num = 1;
1695
+ num++;
1696
+ --num;
1697
+
1698
+ let sum = 0;
1699
+ let truthyCount = 0;
1700
+ for (let i = 0; i < array.length; i++) {
1701
+ let value = array[i];
1702
+ sum += value;
1703
+ if (value) {
1704
+ truthyCount++;
1705
+ }
1706
+ }
1707
+
1708
+ // good
1709
+ const array = [1, 2, 3];
1710
+ let num = 1;
1711
+ num += 1;
1712
+ num -= 1;
1713
+
1714
+ const sum = array.reduce((a, b) => (a + b), 0);
1715
+ const truthyCount = array.filter(Boolean).length;
1716
+ ```
1717
+
1718
+ <a name="variables--linebreak"></a>
1719
+ - [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).
1720
+
1721
+ > Why? Linebreaks surrounding `=` can obfuscate the value of an assignment.
1722
+
1723
+ ```javascript
1724
+ // bad
1725
+ const foo =
1726
+ superLongLongLongLongLongLongLongLongFunctionName();
1727
+
1728
+ // bad
1729
+ const foo
1730
+ = 'superLongLongLongLongLongLongLongLongString';
1731
+
1732
+ // good
1733
+ const foo = (
1734
+ superLongLongLongLongLongLongLongLongFunctionName()
1735
+ );
1736
+
1737
+ // good
1738
+ const foo = 'superLongLongLongLongLongLongLongLongString';
1739
+ ```
1740
+
1741
+ <a name="variables--no-unused-vars"></a>
1742
+ - [13.8](#variables--no-unused-vars) Disallow unused variables. eslint: [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars)
1743
+
1744
+ > 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.
1745
+
1746
+ ```javascript
1747
+ // bad
1748
+ var some_unused_var = 42;
1749
+
1750
+ // Write-only variables are not considered as used.
1751
+ var y = 10;
1752
+ y = 5;
1753
+
1754
+ // A read for a modification of itself is not considered as used.
1755
+ var z = 0;
1756
+ z = z + 1;
1757
+
1758
+ // Unused function arguments.
1759
+ function getX(x, y) {
1760
+ return x;
1761
+ }
1762
+
1763
+ // good
1764
+ function getXPlusY(x, y) {
1765
+ return x + y;
1766
+ }
1767
+
1768
+ var x = 1;
1769
+ var y = a + 2;
1770
+
1771
+ alert(getXPlusY(x, y));
1772
+
1773
+ // 'type' is ignored even if unused because it has a rest property sibling.
1774
+ // This is a form of extracting an object that omits the specified keys.
1775
+ var { type, ...coords } = data;
1776
+ // 'coords' is now the 'data' object without its 'type' property.
1777
+ ```
1778
+
1779
+ **[⬆ back to top](#table-of-contents)**
1780
+
1781
+ ## Hoisting
1782
+
1783
+ <a name="hoisting--about"></a>
1784
+ - [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](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15).
1785
+
1786
+ ```javascript
1787
+ // we know this wouldn’t work (assuming there
1788
+ // is no notDefined global variable)
1789
+ function example() {
1790
+ console.log(notDefined); // => throws a ReferenceError
1791
+ }
1792
+
1793
+ // creating a variable declaration after you
1794
+ // reference the variable will work due to
1795
+ // variable hoisting. Note: the assignment
1796
+ // value of `true` is not hoisted.
1797
+ function example() {
1798
+ console.log(declaredButNotAssigned); // => undefined
1799
+ var declaredButNotAssigned = true;
1800
+ }
1801
+
1802
+ // the interpreter is hoisting the variable
1803
+ // declaration to the top of the scope,
1804
+ // which means our example could be rewritten as:
1805
+ function example() {
1806
+ let declaredButNotAssigned;
1807
+ console.log(declaredButNotAssigned); // => undefined
1808
+ declaredButNotAssigned = true;
1809
+ }
1810
+
1811
+ // using const and let
1812
+ function example() {
1813
+ console.log(declaredButNotAssigned); // => throws a ReferenceError
1814
+ console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
1815
+ const declaredButNotAssigned = true;
1816
+ }
1817
+ ```
1818
+
1819
+ <a name="hoisting--anon-expressions"></a>
1820
+ - [14.2](#hoisting--anon-expressions) Anonymous function expressions hoist their variable name, but not the function assignment.
1821
+
1822
+ ```javascript
1823
+ function example() {
1824
+ console.log(anonymous); // => undefined
1825
+
1826
+ anonymous(); // => TypeError anonymous is not a function
1827
+
1828
+ var anonymous = function () {
1829
+ console.log('anonymous function expression');
1830
+ };
1831
+ }
1832
+ ```
1833
+
1834
+ <a name="hoisting--named-expresions"></a><a name="hoisting--named-expressions"></a>
1835
+ - [14.3](#hoisting--named-expressions) Named function expressions hoist the variable name, not the function name or the function body.
1836
+
1837
+ ```javascript
1838
+ function example() {
1839
+ console.log(named); // => undefined
1840
+
1841
+ named(); // => TypeError named is not a function
1842
+
1843
+ superPower(); // => ReferenceError superPower is not defined
1844
+
1845
+ var named = function superPower() {
1846
+ console.log('Flying');
1847
+ };
1848
+ }
1849
+
1850
+ // the same is true when the function name
1851
+ // is the same as the variable name.
1852
+ function example() {
1853
+ console.log(named); // => undefined
1854
+
1855
+ named(); // => TypeError named is not a function
1856
+
1857
+ var named = function named() {
1858
+ console.log('named');
1859
+ };
1860
+ }
1861
+ ```
1862
+
1863
+ <a name="hoisting--declarations"></a>
1864
+ - [14.4](#hoisting--declarations) Function declarations hoist their name and the function body.
1865
+
1866
+ ```javascript
1867
+ function example() {
1868
+ superPower(); // => Flying
1869
+
1870
+ function superPower() {
1871
+ console.log('Flying');
1872
+ }
1873
+ }
1874
+ ```
1875
+
1876
+ - 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/).
1877
+
1878
+ **[⬆ back to top](#table-of-contents)**
1879
+
1880
+ ## Comparison Operators & Equality
1881
+
1882
+ <a name="comparison--eqeqeq"></a>
1883
+ - [15.1](#comparison--eqeqeq) Use `===` and `!==` over `==` and `!=`. eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq.html)
1884
+
1885
+ <a name="comparison--if"></a>
1886
+ - [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:
1887
+
1888
+ - **Objects** evaluate to **true**
1889
+ - **Undefined** evaluates to **false**
1890
+ - **Null** evaluates to **false**
1891
+ - **Booleans** evaluate to **the value of the boolean**
1892
+ - **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true**
1893
+ - **Strings** evaluate to **false** if an empty string `''`, otherwise **true**
1894
+
1895
+ ```javascript
1896
+ if ([0] && []) {
1897
+ // true
1898
+ // an array (even an empty one) is an object, objects will evaluate to true
1899
+ }
1900
+ ```
1901
+
1902
+ <a name="comparison--switch-blocks"></a>
1903
+ - [15.3](#comparison--switch-blocks) Always use braces to create blocks in `case` and `default` clauses within switch statements. eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations.html)
1904
+
1905
+ > 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.
1906
+
1907
+ ```javascript
1908
+ // bad
1909
+ switch (foo) {
1910
+ case 1:
1911
+ let x = 1;
1912
+ break;
1913
+ case 2:
1914
+ const y = 2;
1915
+ break;
1916
+ case 3:
1917
+ function f() {
1918
+ // ...
1919
+ }
1920
+ break;
1921
+ default:
1922
+ class C {}
1923
+ }
1924
+
1925
+ // good
1926
+ switch (foo) {
1927
+ case 1: {
1928
+ let x = 1;
1929
+ break;
1930
+ }
1931
+ case 2: {
1932
+ const y = 2;
1933
+ break;
1934
+ }
1935
+ case 3: {
1936
+ function f() {
1937
+ // ...
1938
+ }
1939
+ break;
1940
+ }
1941
+ case 4: {
1942
+ bar();
1943
+ break;
1944
+ }
1945
+ default: {
1946
+ class C {}
1947
+ }
1948
+ }
1949
+ ```
1950
+
1951
+ <a name="comparison--nested-ternaries"></a>
1952
+ - [15.4](#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)
1953
+
1954
+ ```javascript
1955
+ // bad
1956
+ const foo = maybe1 > maybe2
1957
+ ? "bar"
1958
+ : value1 > value2 ? "baz" : null;
1959
+
1960
+ // split into 2 separated ternary expressions
1961
+ const maybeNull = value1 > value2 ? 'baz' : null;
1962
+
1963
+ // good
1964
+ const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1965
+ ```
1966
+
1967
+ <a name="comparison--unneeded-ternary"></a>
1968
+ - [15.5](#comparison--unneeded-ternary) Avoid unneeded ternary statements. eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary.html)
1969
+
1970
+ ```javascript
1971
+ // bad
1972
+ const foo = a ? a : b;
1973
+ const bar = c ? true : false;
1974
+ const baz = c ? false : true;
1975
+
1976
+ // good
1977
+ const foo = a || b;
1978
+ const bar = Boolean(c);
1979
+ const baz = !c;
1980
+ ```
1981
+
1982
+ <a name="comparison--no-mixed-operators"></a>
1983
+ - [15.6](#comparison--no-mixed-operators) When mixing operators, enclose them in parentheses. eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators.html)
1984
+
1985
+ > Why? This improves readability and clarifies the developer’s intention.
1986
+
1987
+ ```javascript
1988
+ // bad
1989
+ const foo = a && b < 0 || c > 0 || d + 1 === 0;
1990
+
1991
+ // bad
1992
+ const bar = a ** b - 5 % d;
1993
+
1994
+ // bad
1995
+ // one may be confused into thinking (a || b) && c
1996
+ if (a || b && c) {
1997
+ return d;
1998
+ }
1999
+
2000
+ // good
2001
+ const foo = (a && (b < 0)) || (c > 0) || ((d + 1) === 0);
2002
+
2003
+ // good
2004
+ const bar = (a ** b) - (5 % d);
2005
+
2006
+ // good
2007
+ if (a || (b && c)) {
2008
+ return d;
2009
+ }
2010
+
2011
+ // good
2012
+ const bar = (a + b) / (c * d);
2013
+ ```
2014
+
2015
+ <a name="comparison--shortcuts"></a>
2016
+ - [15.7](#comparison--shortcuts) Use shortcuts for booleans, but explicit comparisons for strings and numbers.
2017
+
2018
+ ```javascript
2019
+ // bad
2020
+ if (isValid === true) {
2021
+ // ...
2022
+ }
2023
+
2024
+ // good
2025
+ if (isValid) {
2026
+ // ...
2027
+ }
2028
+
2029
+ // bad
2030
+ if (name) {
2031
+ // ...
2032
+ }
2033
+
2034
+ // good
2035
+ if (name !== '') {
2036
+ // ...
2037
+ }
2038
+
2039
+ // bad
2040
+ if (collection.length) {
2041
+ // ...
2042
+ }
2043
+
2044
+ // good
2045
+ if (collection.length > 0) {
2046
+ // ...
2047
+ }
2048
+ ```
2049
+
2050
+ **[⬆ back to top](#table-of-contents)**
2051
+
2052
+ ## Blocks
2053
+
2054
+ <a name="blocks--braces"></a>
2055
+ - [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)
2056
+
2057
+ ```javascript
2058
+ // bad
2059
+ if (test)
2060
+ return false;
2061
+
2062
+ // good
2063
+ if (test) return false;
2064
+
2065
+ // good
2066
+ if (test) {
2067
+ return false;
2068
+ }
2069
+
2070
+ // ok
2071
+ function foo() { return false; }
2072
+
2073
+ // good
2074
+ function bar() {
2075
+ return false;
2076
+ }
2077
+ ```
2078
+
2079
+ <a name="blocks--cuddled-elses"></a>
2080
+ - [16.2](#blocks--cuddled-elses) If you’re using multi-line blocks with `if` and `else`, put `else` on the same line as your `if` block’s closing brace. eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style.html)
2081
+
2082
+ ```javascript
2083
+ // bad
2084
+ if (test) {
2085
+ thing1();
2086
+ thing2();
2087
+ }
2088
+ else {
2089
+ thing3();
2090
+ }
2091
+
2092
+ // good
2093
+ if (test) {
2094
+ thing1();
2095
+ thing2();
2096
+ } else {
2097
+ thing3();
2098
+ }
2099
+ ```
2100
+
2101
+ <a name="blocks--no-else-return"></a>
2102
+ - [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)
2103
+
2104
+ ```javascript
2105
+ // bad
2106
+ function foo() {
2107
+ if (x) {
2108
+ return x;
2109
+ } else {
2110
+ return y;
2111
+ }
2112
+ }
2113
+
2114
+ // bad
2115
+ function cats() {
2116
+ if (x) {
2117
+ return x;
2118
+ } else if (y) {
2119
+ return y;
2120
+ }
2121
+ }
2122
+
2123
+ // bad
2124
+ function dogs() {
2125
+ if (x) {
2126
+ return x;
2127
+ } else {
2128
+ if (y) {
2129
+ return y;
2130
+ }
2131
+ }
2132
+ }
2133
+
2134
+ // good
2135
+ function foo() {
2136
+ if (x) {
2137
+ return x;
2138
+ }
2139
+
2140
+ return y;
2141
+ }
2142
+
2143
+ // good
2144
+ function cats() {
2145
+ if (x) {
2146
+ return x;
2147
+ }
2148
+
2149
+ if (y) {
2150
+ return y;
2151
+ }
2152
+ }
2153
+
2154
+ // good
2155
+ function dogs(x) {
2156
+ if (x) {
2157
+ if (z) {
2158
+ return y;
2159
+ }
2160
+ } else {
2161
+ return z;
2162
+ }
2163
+ }
2164
+ ```
2165
+
2166
+ **[⬆ back to top](#table-of-contents)**
2167
+
2168
+ ## Control Statements
2169
+
2170
+ <a name="control-statements"></a>
2171
+ - [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.
2172
+
2173
+ > 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.
2174
+
2175
+ ```javascript
2176
+ // bad
2177
+ if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
2178
+ thing1();
2179
+ }
2180
+
2181
+ // bad
2182
+ if (foo === 123 &&
2183
+ bar === 'abc') {
2184
+ thing1();
2185
+ }
2186
+
2187
+ // bad
2188
+ if (foo === 123
2189
+ && bar === 'abc') {
2190
+ thing1();
2191
+ }
2192
+
2193
+ // ok
2194
+ if (
2195
+ foo === 123 &&
2196
+ bar === 'abc'
2197
+ ) {
2198
+ thing1();
2199
+ }
2200
+
2201
+ // good
2202
+ if (
2203
+ foo === 123
2204
+ && bar === 'abc'
2205
+ ) {
2206
+ thing1();
2207
+ }
2208
+
2209
+ // good
2210
+ if (
2211
+ (foo === 123 || bar === 'abc')
2212
+ && doesItLookGoodWhenItBecomesThatLong()
2213
+ && isThisReallyHappening()
2214
+ ) {
2215
+ thing1();
2216
+ }
2217
+
2218
+ // good
2219
+ if (foo === 123 && bar === 'abc') {
2220
+ thing1();
2221
+ }
2222
+ ```
2223
+
2224
+ <a name="control-statement--value-selection"></a><a name="control-statements--value-selection"></a>
2225
+ - [17.2](#control-statements--value-selection) Don't use selection operators in place of control statements.
2226
+
2227
+ ```javascript
2228
+ // bad
2229
+ !isRunning && startRunning();
2230
+
2231
+ // good
2232
+ if (!isRunning) {
2233
+ startRunning();
2234
+ }
2235
+ ```
2236
+
2237
+ **[⬆ back to top](#table-of-contents)**
2238
+
2239
+ ## Comments
2240
+ <a name="comments--singleline"></a>
2241
+ - [18.1](#comments--singleline) Prefer to place 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.
2242
+
2243
+ ```javascript
2244
+ // ok
2245
+ const active = true; // is current tab
2246
+
2247
+ // good
2248
+ // is current tab
2249
+ const active = true;
2250
+
2251
+ // bad
2252
+ function getType() {
2253
+ console.log('fetching type...');
2254
+ // set the default type to 'no type'
2255
+ const type = this.type || 'no type';
2256
+
2257
+ return type;
2258
+ }
2259
+
2260
+ // good
2261
+ function getType() {
2262
+ console.log('fetching type...');
2263
+
2264
+ // set the default type to 'no type'
2265
+ const type = this.type || 'no type';
2266
+
2267
+ return type;
2268
+ }
2269
+
2270
+ // also good
2271
+ function getType() {
2272
+ // set the default type to 'no type'
2273
+ const type = this.type || 'no type';
2274
+
2275
+ return type;
2276
+ }
2277
+ ```
2278
+
2279
+ <a name="comments--spaces"></a>
2280
+ - [18.2](#comments--spaces) Start all comments with a space to make it easier to read. eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment)
2281
+
2282
+ ```javascript
2283
+ // bad
2284
+ //is current tab
2285
+ const active = true;
2286
+
2287
+ // good
2288
+ // is current tab
2289
+ const active = true;
2290
+ ```
2291
+
2292
+ **[⬆ back to top](#table-of-contents)**
2293
+
2294
+ ## Whitespace
2295
+
2296
+ <a name="whitespace--spaces"></a>
2297
+ - [19.1](#whitespace--spaces) Use soft tabs (space character) set to 2 spaces. eslint: [`indent`](https://eslint.org/docs/rules/indent.html)
2298
+
2299
+ ```javascript
2300
+ // bad
2301
+ function foo() {
2302
+ ∙∙∙∙let name;
2303
+ }
2304
+
2305
+ // bad
2306
+ function bar() {
2307
+ ∙let name;
2308
+ }
2309
+
2310
+ // good
2311
+ function baz() {
2312
+ ∙∙let name;
2313
+ }
2314
+ ```
2315
+
2316
+ <a name="whitespace--before-blocks"></a>
2317
+ - [19.2](#whitespace--before-blocks) Place 1 space before the leading brace. eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks.html)
2318
+
2319
+ ```javascript
2320
+ // bad
2321
+ function test(){
2322
+ console.log('test');
2323
+ }
2324
+
2325
+ // good
2326
+ function test() {
2327
+ console.log('test');
2328
+ }
2329
+
2330
+ // bad
2331
+ dog.set('attr',{
2332
+ age: '1 year',
2333
+ breed: 'Bernese Mountain Dog',
2334
+ });
2335
+
2336
+ // good
2337
+ dog.set('attr', {
2338
+ age: '1 year',
2339
+ breed: 'Bernese Mountain Dog',
2340
+ });
2341
+ ```
2342
+
2343
+ <a name="whitespace--around-keywords"></a>
2344
+ - [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)
2345
+
2346
+ ```javascript
2347
+ // bad
2348
+ if(isJedi) {
2349
+ fight ();
2350
+ }
2351
+
2352
+ // good
2353
+ if (isJedi) {
2354
+ fight();
2355
+ }
2356
+
2357
+ // bad
2358
+ function fight () {
2359
+ console.log ('Swooosh!');
2360
+ }
2361
+
2362
+ // good
2363
+ function fight() {
2364
+ console.log('Swooosh!');
2365
+ }
2366
+ ```
2367
+
2368
+ <a name="whitespace--infix-ops"></a>
2369
+ - [19.4](#whitespace--infix-ops) Set off operators with spaces. eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops.html)
2370
+
2371
+ ```javascript
2372
+ // bad
2373
+ const x=y+5;
2374
+
2375
+ // good
2376
+ const x = y + 5;
2377
+ ```
2378
+
2379
+ <a name="whitespace--after-blocks"></a>
2380
+ - [19.5](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement.
2381
+
2382
+ ```javascript
2383
+ // bad
2384
+ if (foo) {
2385
+ return bar;
2386
+ }
2387
+ return baz;
2388
+
2389
+ // good
2390
+ if (foo) {
2391
+ return bar;
2392
+ }
2393
+
2394
+ return baz;
2395
+
2396
+ // bad
2397
+ const obj = {
2398
+ foo() {
2399
+ },
2400
+ bar() {
2401
+ },
2402
+ };
2403
+ return obj;
2404
+
2405
+ // good
2406
+ const obj = {
2407
+ foo() {
2408
+ },
2409
+
2410
+ bar() {
2411
+ },
2412
+ };
2413
+
2414
+ return obj;
2415
+
2416
+ // bad
2417
+ const arr = [
2418
+ function foo() {
2419
+ },
2420
+ function bar() {
2421
+ },
2422
+ ];
2423
+ return arr;
2424
+
2425
+ // good
2426
+ const arr = [
2427
+ function foo() {
2428
+ },
2429
+
2430
+ function bar() {
2431
+ },
2432
+ ];
2433
+
2434
+ return arr;
2435
+ ```
2436
+
2437
+ <a name="whitespace--padded-blocks"></a>
2438
+ - [19.6](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](https://eslint.org/docs/rules/padded-blocks.html)
2439
+
2440
+ ```javascript
2441
+ // bad
2442
+ function bar() {
2443
+
2444
+ console.log(foo);
2445
+
2446
+ }
2447
+
2448
+ // bad
2449
+ if (baz) {
2450
+
2451
+ console.log(qux);
2452
+ } else {
2453
+ console.log(foo);
2454
+
2455
+ }
2456
+
2457
+ // bad
2458
+ class Foo {
2459
+
2460
+ constructor(bar) {
2461
+ this.bar = bar;
2462
+ }
2463
+ }
2464
+
2465
+ // good
2466
+ function bar() {
2467
+ console.log(foo);
2468
+ }
2469
+
2470
+ // good
2471
+ if (baz) {
2472
+ console.log(qux);
2473
+ } else {
2474
+ console.log(foo);
2475
+ }
2476
+ ```
2477
+
2478
+ <a name="whitespace--in-parens"></a>
2479
+ - [19.7](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](https://eslint.org/docs/rules/space-in-parens.html)
2480
+
2481
+ ```javascript
2482
+ // bad
2483
+ function bar( foo ) {
2484
+ return foo;
2485
+ }
2486
+
2487
+ // good
2488
+ function bar(foo) {
2489
+ return foo;
2490
+ }
2491
+
2492
+ // bad
2493
+ if ( foo ) {
2494
+ console.log(foo);
2495
+ }
2496
+
2497
+ // good
2498
+ if (foo) {
2499
+ console.log(foo);
2500
+ }
2501
+ ```
2502
+
2503
+ <a name="whitespace--in-brackets"></a>
2504
+ - [19.8](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](https://eslint.org/docs/rules/array-bracket-spacing.html)
2505
+
2506
+ ```javascript
2507
+ // bad
2508
+ const foo = [ 1, 2, 3 ];
2509
+ console.log(foo[ 0 ]);
2510
+
2511
+ // good
2512
+ const foo = [1, 2, 3];
2513
+ console.log(foo[0]);
2514
+ ```
2515
+
2516
+ <a name="whitespace--in-braces"></a>
2517
+ - [19.9](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing.html)
2518
+
2519
+ ```javascript
2520
+ // bad
2521
+ const foo = {clark: 'kent'};
2522
+
2523
+ // good
2524
+ const foo = { clark: 'kent' };
2525
+ ```
2526
+
2527
+ <a name="whitespace--max-len"></a>
2528
+ - [19.10](#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)
2529
+
2530
+ > Why? This ensures readability and maintainability.
2531
+
2532
+ ```javascript
2533
+ // bad
2534
+ const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
2535
+
2536
+ // bad
2537
+ $.ajax({ method: 'POST', url: 'https://google.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
2538
+
2539
+ // good
2540
+ const foo = jsonData
2541
+ && jsonData.foo
2542
+ && jsonData.foo.bar
2543
+ && jsonData.foo.bar.baz
2544
+ && jsonData.foo.bar.baz.quux
2545
+ && jsonData.foo.bar.baz.quux.xyzzy;
2546
+
2547
+ // good
2548
+ $.ajax({
2549
+ method: 'POST',
2550
+ url: 'https://google.com/',
2551
+ data: { name: 'John' },
2552
+ })
2553
+ .done(() => console.log('Congratulations!'))
2554
+ .fail(() => console.log('You have failed this city.'));
2555
+ ```
2556
+
2557
+ <a name="whitespace--block-spacing"></a>
2558
+ - [19.11](#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)
2559
+
2560
+ ```javascript
2561
+ // bad
2562
+ function foo() {return true;}
2563
+ if (foo) { bar = 0;}
2564
+
2565
+ // good
2566
+ function foo() { return true; }
2567
+ if (foo) { bar = 0; }
2568
+ ```
2569
+
2570
+ <a name="whitespace--comma-spacing"></a>
2571
+ - [19.12](#whitespace--comma-spacing) Avoid spaces before commas and require a space after commas. eslint: [`comma-spacing`](https://eslint.org/docs/rules/comma-spacing)
2572
+
2573
+ ```javascript
2574
+ // bad
2575
+ var foo = 1,bar = 2;
2576
+ var arr = [1 , 2];
2577
+
2578
+ // good
2579
+ var foo = 1, bar = 2;
2580
+ var arr = [1, 2];
2581
+ ```
2582
+
2583
+ <a name="whitespace--computed-property-spacing"></a>
2584
+ - [19.13](#whitespace--computed-property-spacing) Enforce spacing inside of computed property brackets. eslint: [`computed-property-spacing`](https://eslint.org/docs/rules/computed-property-spacing)
2585
+
2586
+ ```javascript
2587
+ // bad
2588
+ obj[foo ]
2589
+ obj[ 'foo']
2590
+ var x = {[ b ]: a}
2591
+ obj[foo[ bar ]]
2592
+
2593
+ // good
2594
+ obj[foo]
2595
+ obj['foo']
2596
+ var x = { [b]: a }
2597
+ obj[foo[bar]]
2598
+ ```
2599
+
2600
+ <a name="whitespace--func-call-spacing"></a>
2601
+ - [19.14](#whitespace--func-call-spacing) Avoid spaces between functions and their invocations. eslint: [`func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing)
2602
+
2603
+ ```javascript
2604
+ // bad
2605
+ func ();
2606
+
2607
+ func
2608
+ ();
2609
+
2610
+ // good
2611
+ func();
2612
+ ```
2613
+
2614
+ <a name="whitespace--key-spacing"></a>
2615
+ - [19.15](#whitespace--key-spacing) Enforce spacing between keys and values in object literal properties. eslint: [`key-spacing`](https://eslint.org/docs/rules/key-spacing)
2616
+
2617
+ ```javascript
2618
+ // bad
2619
+ var obj = { "foo" : 42 };
2620
+ var obj2 = { "foo":42 };
2621
+
2622
+ // good
2623
+ var obj = { "foo": 42 };
2624
+ ```
2625
+
2626
+ <a name="whitespace--no-trailing-spaces"></a>
2627
+ - [19.16](#whitespace--no-trailing-spaces) Avoid trailing spaces at the end of lines. eslint: [`no-trailing-spaces`](https://eslint.org/docs/rules/no-trailing-spaces)
2628
+
2629
+ <a name="whitespace--no-multiple-empty-lines"></a>
2630
+ - [19.17](#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)
2631
+
2632
+ ```javascript
2633
+ // bad
2634
+ var x = 1;
2635
+
2636
+
2637
+
2638
+ var y = 2;
2639
+
2640
+ // good
2641
+ var x = 1;
2642
+
2643
+ var y = 2;
2644
+ ```
2645
+
2646
+ **[⬆ back to top](#table-of-contents)**
2647
+
2648
+ ## Commas
2649
+
2650
+ <a name="commas--leading-trailing"></a>
2651
+ - [20.1](#commas--leading-trailing) Leading commas: **Nope.** eslint: [`comma-style`](https://eslint.org/docs/rules/comma-style.html)
2652
+
2653
+ ```javascript
2654
+ // bad
2655
+ const story = [
2656
+ once
2657
+ , upon
2658
+ , aTime
2659
+ ];
2660
+
2661
+ // good
2662
+ const story = [
2663
+ once,
2664
+ upon,
2665
+ aTime,
2666
+ ];
2667
+
2668
+ // bad
2669
+ const hero = {
2670
+ firstName: 'Ada'
2671
+ , lastName: 'Lovelace'
2672
+ , birthYear: 1815
2673
+ , superPower: 'computers'
2674
+ };
2675
+
2676
+ // good
2677
+ const hero = {
2678
+ firstName: 'Ada',
2679
+ lastName: 'Lovelace',
2680
+ birthYear: 1815,
2681
+ superPower: 'computers',
2682
+ };
2683
+ ```
2684
+
2685
+ <a name="commas--dangling"></a>
2686
+ - [20.2](#commas--dangling) Additional trailing comma: **Yup.** eslint: [`comma-dangle`](https://eslint.org/docs/rules/comma-dangle.html)
2687
+
2688
+ > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the [trailing comma problem](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas) in legacy browsers.
2689
+
2690
+ ```diff
2691
+ // bad - git diff without trailing comma
2692
+ const hero = {
2693
+ firstName: 'Florence',
2694
+ - lastName: 'Nightingale'
2695
+ + lastName: 'Nightingale',
2696
+ + inventorOf: ['coxcomb chart', 'modern nursing']
2697
+ };
2698
+
2699
+ // good - git diff with trailing comma
2700
+ const hero = {
2701
+ firstName: 'Florence',
2702
+ lastName: 'Nightingale',
2703
+ + inventorOf: ['coxcomb chart', 'modern nursing'],
2704
+ };
2705
+ ```
2706
+
2707
+ ```javascript
2708
+ // bad
2709
+ const hero = {
2710
+ firstName: 'Dana',
2711
+ lastName: 'Scully'
2712
+ };
2713
+
2714
+ const heroes = [
2715
+ 'Batman',
2716
+ 'Superman'
2717
+ ];
2718
+
2719
+ // good
2720
+ const hero = {
2721
+ firstName: 'Dana',
2722
+ lastName: 'Scully',
2723
+ };
2724
+
2725
+ const heroes = [
2726
+ 'Batman',
2727
+ 'Superman',
2728
+ ];
2729
+
2730
+ // bad
2731
+ function createHero(
2732
+ firstName,
2733
+ lastName,
2734
+ inventorOf
2735
+ ) {
2736
+ // does nothing
2737
+ }
2738
+
2739
+ // good
2740
+ function createHero(
2741
+ firstName,
2742
+ lastName,
2743
+ inventorOf,
2744
+ ) {
2745
+ // does nothing
2746
+ }
2747
+
2748
+ // good (note that a comma must not appear after a "rest" element)
2749
+ function createHero(
2750
+ firstName,
2751
+ lastName,
2752
+ inventorOf,
2753
+ ...heroArgs
2754
+ ) {
2755
+ // does nothing
2756
+ }
2757
+
2758
+ // bad
2759
+ createHero(
2760
+ firstName,
2761
+ lastName,
2762
+ inventorOf
2763
+ );
2764
+
2765
+ // good
2766
+ createHero(
2767
+ firstName,
2768
+ lastName,
2769
+ inventorOf,
2770
+ );
2771
+
2772
+ // good (note that a comma must not appear after a "rest" element)
2773
+ createHero(
2774
+ firstName,
2775
+ lastName,
2776
+ inventorOf,
2777
+ ...heroArgs
2778
+ );
2779
+ ```
2780
+
2781
+ **[⬆ back to top](#table-of-contents)**
2782
+
2783
+ ## Semicolons
2784
+
2785
+ <a name="semicolons--required"></a>
2786
+ - [21.1](#semicolons--required) **Yup.** eslint: [`semi`](https://eslint.org/docs/rules/semi.html)
2787
+
2788
+ > 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.
2789
+
2790
+ ```javascript
2791
+ // bad - raises exception
2792
+ const luke = {}
2793
+ const leia = {}
2794
+ [luke, leia].forEach((jedi) => jedi.father = 'vader')
2795
+
2796
+ // bad - raises exception
2797
+ const reaction = "No! That’s impossible!"
2798
+ (async function meanwhileOnTheFalcon() {
2799
+ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
2800
+ // ...
2801
+ }())
2802
+
2803
+ // bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
2804
+ function foo() {
2805
+ return
2806
+ 'search your feelings, you know it to be foo'
2807
+ }
2808
+
2809
+ // good
2810
+ const luke = {};
2811
+ const leia = {};
2812
+ [luke, leia].forEach((jedi) => {
2813
+ jedi.father = 'vader';
2814
+ });
2815
+
2816
+ // good
2817
+ const reaction = "No! That’s impossible!";
2818
+ (async function meanwhileOnTheFalcon() {
2819
+ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
2820
+ // ...
2821
+ }());
2822
+
2823
+ // good
2824
+ function foo() {
2825
+ return 'search your feelings, you know it to be foo';
2826
+ }
2827
+ ```
2828
+
2829
+ [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214#7365214).
2830
+
2831
+ **[⬆ back to top](#table-of-contents)**
2832
+
2833
+ ## Type Casting & Coercion
2834
+
2835
+ <a name="coercion--explicit"></a>
2836
+ - [22.1](#coercion--explicit) Perform type coercion at the beginning of the statement.
2837
+
2838
+ <a name="coercion--strings"></a>
2839
+ - [22.2](#coercion--strings) Strings: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2840
+
2841
+ ```javascript
2842
+ // => this.reviewScore = 9;
2843
+
2844
+ // bad
2845
+ const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
2846
+
2847
+ // bad
2848
+ const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
2849
+
2850
+ // ok
2851
+ const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
2852
+
2853
+ // good
2854
+ const totalScore = String(this.reviewScore);
2855
+ ```
2856
+
2857
+ <a name="coercion--numbers"></a>
2858
+ - [22.3](#coercion--numbers) Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. eslint: [`radix`](https://eslint.org/docs/rules/radix) [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2859
+
2860
+ ```javascript
2861
+ const inputValue = '4';
2862
+
2863
+ // bad
2864
+ const val = new Number(inputValue);
2865
+
2866
+ // bad
2867
+ const val = +inputValue;
2868
+
2869
+ // bad
2870
+ const val = inputValue >> 0;
2871
+
2872
+ // bad
2873
+ const val = parseInt(inputValue);
2874
+
2875
+ // ok
2876
+ const val = Number(inputValue);
2877
+
2878
+ // good
2879
+ const val = parseInt(inputValue, 10);
2880
+ ```
2881
+
2882
+ <a name="coercion--comment-deviations"></a>
2883
+ - [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.
2884
+
2885
+ ```javascript
2886
+ // good
2887
+ /**
2888
+ * parseInt was the reason my code was slow.
2889
+ * Bitshifting the String to coerce it to a
2890
+ * Number made it a lot faster.
2891
+ */
2892
+ const val = inputValue >> 0;
2893
+ ```
2894
+
2895
+ <a name="coercion--bitwise"></a>
2896
+ - [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. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647:
2897
+
2898
+ ```javascript
2899
+ 2147483647 >> 0; // => 2147483647
2900
+ 2147483648 >> 0; // => -2147483648
2901
+ 2147483649 >> 0; // => -2147483647
2902
+ ```
2903
+
2904
+ <a name="coercion--booleans"></a>
2905
+ - [22.6](#coercion--booleans) Booleans: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2906
+
2907
+ ```javascript
2908
+ const age = 0;
2909
+
2910
+ // bad
2911
+ const hasAge = new Boolean(age);
2912
+
2913
+ // good
2914
+ const hasAge = Boolean(age);
2915
+ ```
2916
+
2917
+ **[⬆ back to top](#table-of-contents)**
2918
+
2919
+ ## Naming Conventions
2920
+
2921
+ <a name="naming--descriptive"></a>
2922
+ - [23.1](#naming--descriptive) Avoid single letter names. Be descriptive with your naming. eslint: [`id-length`](https://eslint.org/docs/rules/id-length)
2923
+
2924
+ ```javascript
2925
+ // bad
2926
+ function q() {
2927
+ // ...
2928
+ }
2929
+
2930
+ // good
2931
+ function query() {
2932
+ // ...
2933
+ }
2934
+ ```
2935
+
2936
+ <a name="naming--camelCase"></a>
2937
+ - [23.2](#naming--camelCase) Prefer camelCase when naming objects, functions, and instances. eslint: [`camelcase`](https://eslint.org/docs/rules/camelcase.html)
2938
+
2939
+ ```javascript
2940
+ // bad
2941
+ const OBJEcttsssss = {};
2942
+ const this_is_my_object = {};
2943
+ function c() {}
2944
+
2945
+ // good
2946
+ const thisIsMyObject = {};
2947
+ function thisIsMyFunction() {}
2948
+ ```
2949
+
2950
+ <a name="naming--PascalCase"></a>
2951
+ - [23.3](#naming--PascalCase) Use PascalCase only when naming constructors or classes. eslint: [`new-cap`](https://eslint.org/docs/rules/new-cap.html)
2952
+
2953
+ ```javascript
2954
+ // bad
2955
+ function user(options) {
2956
+ this.name = options.name;
2957
+ }
2958
+
2959
+ const bad = new user({
2960
+ name: 'nope',
2961
+ });
2962
+
2963
+ // good
2964
+ class User {
2965
+ constructor(options) {
2966
+ this.name = options.name;
2967
+ }
2968
+ }
2969
+
2970
+ const good = new User({
2971
+ name: 'yup',
2972
+ });
2973
+ ```
2974
+
2975
+ <a name="naming--self-this"></a>
2976
+ - [23.4](#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).
2977
+
2978
+ ```javascript
2979
+ // bad
2980
+ function foo() {
2981
+ const that = this;
2982
+ return function () {
2983
+ console.log(that);
2984
+ };
2985
+ }
2986
+
2987
+ // good
2988
+ function foo() {
2989
+ return () => {
2990
+ console.log(this);
2991
+ };
2992
+ }
2993
+ ```
2994
+
2995
+ <a name="naming--uppercase"></a>
2996
+ - [23.5](#naming--uppercase) Uppercase business logic constants that never change.
2997
+
2998
+ ```javascript
2999
+ // bad
3000
+ export const valueTypeID = 42;
3001
+
3002
+ // good
3003
+ export const VALUE_TYPE_ID = 42;
3004
+ ```
3005
+
3006
+ **[⬆ back to top](#table-of-contents)**
3007
+
3008
+ ## Accessors
3009
+
3010
+ <a name="accessors--not-required"></a>
3011
+ - [24.1](#accessors--not-required) Accessor functions for properties are not required.
3012
+
3013
+ <a name="accessors--no-getters-setters"></a>
3014
+ - [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('hello')`.
3015
+
3016
+ ```javascript
3017
+ // bad
3018
+ class Dragon {
3019
+ get age() {
3020
+ // ...
3021
+ }
3022
+
3023
+ set age(value) {
3024
+ // ...
3025
+ }
3026
+ }
3027
+
3028
+ // good
3029
+ class Dragon {
3030
+ getAge() {
3031
+ // ...
3032
+ }
3033
+
3034
+ setAge(value) {
3035
+ // ...
3036
+ }
3037
+ }
3038
+ ```
3039
+
3040
+ <a name="accessors--boolean-prefix"></a>
3041
+ - [24.3](#accessors--boolean-prefix) If the property/method is a `boolean`, use `isXXX()` or `hasXXX()`.
3042
+
3043
+ ```javascript
3044
+ // bad
3045
+ if (!dragon.age()) {
3046
+ return false;
3047
+ }
3048
+
3049
+ // good
3050
+ if (!dragon.hasAge()) {
3051
+ return false;
3052
+ }
3053
+ ```
3054
+
3055
+ <a name="accessors--consistent"></a>
3056
+ - [24.4](#accessors--consistent) It’s okay to create `get()` and `set()` functions, but be consistent.
3057
+
3058
+ ```javascript
3059
+ class Jedi {
3060
+ constructor(options = {}) {
3061
+ const lightsaber = options.lightsaber || 'blue';
3062
+ this.set('lightsaber', lightsaber);
3063
+ }
3064
+
3065
+ set(key, val) {
3066
+ this[key] = val;
3067
+ }
3068
+
3069
+ get(key) {
3070
+ return this[key];
3071
+ }
3072
+ }
3073
+ ```
3074
+
3075
+ **[⬆ back to top](#table-of-contents)**
3076
+
3077
+ ## Events
3078
+
3079
+ <a name="events--hash"></a>
3080
+ - [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:
3081
+
3082
+ ```javascript
3083
+ // bad
3084
+ $(this).trigger('listingUpdated', listing.id);
3085
+
3086
+ // ...
3087
+
3088
+ $(this).on('listingUpdated', (e, listingID) => {
3089
+ // do something with listingID
3090
+ });
3091
+ ```
3092
+
3093
+ prefer:
3094
+
3095
+ ```javascript
3096
+ // good
3097
+ $(this).trigger('listingUpdated', { listingID: listing.id });
3098
+
3099
+ // ...
3100
+
3101
+ $(this).on('listingUpdated', (e, data) => {
3102
+ // do something with data.listingID
3103
+ });
3104
+ ```
3105
+
3106
+ **[⬆ back to top](#table-of-contents)**
3107
+
3108
+ ## jQuery
3109
+
3110
+ <a name="jquery--dollar-prefix"></a>
3111
+ - [26.1](#jquery--dollar-prefix) Prefix jQuery object variables with a `$`.
3112
+
3113
+ ```javascript
3114
+ // bad
3115
+ const sidebar = $('.sidebar');
3116
+
3117
+ // good
3118
+ const $sidebar = $('.sidebar');
3119
+
3120
+ // good
3121
+ const $sidebarBtn = $('.sidebar-btn');
3122
+ ```
3123
+
3124
+ <a name="jquery--cache"></a>
3125
+ - [26.2](#jquery--cache) Cache jQuery lookups.
3126
+
3127
+ ```javascript
3128
+ // bad
3129
+ function setSidebar() {
3130
+ $('.sidebar').hide();
3131
+
3132
+ // ...
3133
+
3134
+ $('.sidebar').css({
3135
+ 'background-color': 'pink',
3136
+ });
3137
+ }
3138
+
3139
+ // good
3140
+ function setSidebar() {
3141
+ const $sidebar = $('.sidebar');
3142
+ $sidebar.hide();
3143
+
3144
+ // ...
3145
+
3146
+ $sidebar.css({
3147
+ 'background-color': 'pink',
3148
+ });
3149
+ }
3150
+ ```
3151
+
3152
+ <a name="jquery--queries"></a>
3153
+ - [26.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)
3154
+
3155
+ <a name="jquery--find"></a>
3156
+ - [26.4](#jquery--find) Use `find` with scoped jQuery object queries.
3157
+
3158
+ ```javascript
3159
+ // bad
3160
+ $('ul', '.sidebar').hide();
3161
+
3162
+ // bad
3163
+ $('.sidebar').find('ul').hide();
3164
+
3165
+ // good
3166
+ $('.sidebar ul').hide();
3167
+
3168
+ // good
3169
+ $('.sidebar > ul').hide();
3170
+
3171
+ // good
3172
+ $sidebar.find('ul').hide();
3173
+ ```
3174
+
3175
+ **[⬆ back to top](#table-of-contents)**
3176
+
3177
+ ## ECMAScript 5 Compatibility
3178
+
3179
+ <a name="es5-compat--kangax"></a>
3180
+ - [27.1](#es5-compat--kangax) Refer to [Kangax](https://twitter.com/kangax/)’s ES5 [compatibility table](https://kangax.github.io/es5-compat-table/).
3181
+
3182
+ **[⬆ back to top](#table-of-contents)**
3183
+
3184
+ <a name="ecmascript-6-styles"></a>
3185
+ ## ECMAScript 6+ (ES 2015+) Styles
3186
+
3187
+ <a name="es6-styles"></a>
3188
+ - [28.1](#es6-styles) This is a collection of links to the various ES6+ features.
3189
+
3190
+ 1. [Arrow Functions](#arrow-functions)
3191
+ 1. [Classes](#classes--constructors)
3192
+ 1. [Object Shorthand](#es6-object-shorthand)
3193
+ 1. [Object Concise](#es6-object-concise)
3194
+ 1. [Object Computed Properties](#es6-computed-properties)
3195
+ 1. [Template Strings](#es6-template-literals)
3196
+ 1. [Destructuring](#destructuring)
3197
+ 1. [Default Parameters](#es6-default-parameters)
3198
+ 1. [Rest](#es6-rest)
3199
+ 1. [Array Spreads](#es6-array-spreads)
3200
+ 1. [Let and Const](#references)
3201
+ 1. [Exponentiation Operator](#es2016-properties--exponentiation-operator)
3202
+ 1. [Iterators and Generators](#iterators-and-generators)
3203
+ 1. [Modules](#modules)
3204
+
3205
+ **[⬆ back to top](#table-of-contents)**
3206
+
3207
+ ## Standard Library
3208
+
3209
+ The [Standard Library](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)
3210
+ contains utilities that are functionally broken but remain for legacy reasons.
3211
+
3212
+ <a name="standard-library--isnan"></a>
3213
+ - [29.1](#standard-library--isnan) Use `Number.isNaN` instead of global `isNaN`.
3214
+ eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
3215
+
3216
+ > Why? The global `isNaN` coerces non-numbers to numbers, returning true for anything that coerces to NaN.
3217
+ > If this behavior is desired, make it explicit.
3218
+
3219
+ ```javascript
3220
+ // bad
3221
+ isNaN('1.2'); // false
3222
+ isNaN('1.2.3'); // true
3223
+
3224
+ // good
3225
+ Number.isNaN('1.2.3'); // false
3226
+ Number.isNaN(Number('1.2.3')); // true
3227
+ ```
3228
+
3229
+ <a name="standard-library--isfinite"></a>
3230
+ - [29.2](#standard-library--isfinite) Use `Number.isFinite` instead of global `isFinite`.
3231
+ eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
3232
+
3233
+ > Why? The global `isFinite` coerces non-numbers to numbers, returning true for anything that coerces to a finite number.
3234
+ > If this behavior is desired, make it explicit.
3235
+
3236
+ ```javascript
3237
+ // bad
3238
+ isFinite('2e3'); // true
3239
+
3240
+ // good
3241
+ Number.isFinite('2e3'); // false
3242
+ Number.isFinite(parseInt('2e3', 10)); // true
3243
+ ```
3244
+
3245
+ **[⬆ back to top](#table-of-contents)**
3246
+
3247
+ ## Testing
3248
+
3249
+ - Whichever testing framework you use, you should be writing tests!
3250
+ - Strive to write many small pure functions, and minimize where mutations occur.
3251
+ - Be cautious about stubs and mocks - they can make your tests more brittle.
3252
+ - 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.
3253
+ - 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.
3254
+
3255
+ **[⬆ back to top](#table-of-contents)**