Skip to content

A collection of JavaScript / React / React Native style guide

License

Notifications You must be signed in to change notification settings

jeffraux/javascript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 

Repository files navigation

JavaScript Style Guide;

A collection of JavaScript / React / React Native style guides

Here's the one for React.

NOTE: This is still a work-in-progress. Everyone can contribute just send a pull request. ;)

Table of Contents

  1. Introduction
  2. Source Files
  3. References
  4. Modules
  5. Objects
  6. Arrays
  7. Destructuring
  8. String
  9. Functions
  10. Arrow Functions
  11. Iterators & Generators

Introduction

  • This style guide is an accumulation of coding standards from different sources (e.g. AirBnB, Google, etc.) as well as my own inputs based on my experiences with JavaScript and React.
  • Optional formatting choices made in examples must not be enforced as rules.

NOTE: this style guide is following the ES6 standard

Source Files

  • Files
    • File names must be all lowercase and may include _ underscores or - dashes, but no additional punctuation. Follow the convention that your project uses. File name's extension must be .js.
  • Special Characters
    • Whitespace - Tab characters are not used for indentation.

References

  • Use const for all of your references; avoid using var.

    Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.

    // avoid
    var a = 1;
    var b = 2;
    
    // prefer
    const a = 1;
    const b = 2;

  • If you must reassign references, use let instead of var.

    Why? let is block-scoped rather than function-scoped like var.

    // avoid
    var name = 'John Doe';
    
    if (true) {
      name = 'Juan Dela Cruz';
    }
    
    // prefer
    let name = 'John Doe';
    
    if (true) {
      name = 'Juan Dela Cruz';
    }

Modules

  • Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system.

    Why? Modules are the future, let’s start using the future now.

    // avoid
    const StyleGuide = require('./StyleGuide');
    module.exports = StyleGuide.Modules;
    
    // this is fine but...
    import StyleGuide from './StyleGuide';
    export default StyleGuide.Modules;
    
    // prefer this
    import { Modules } from './StyleGuide';
    export default Modules;

  • Do not use wildcard imports.

    Why? This makes sure you have a single default export.

    // avoid
    import * as StyleGuide from './StyleGuide';
    
    // prefer
    import StyleGuide from './StyleGuide';

  • And do not export directly from an import.

    Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.

    // avoid
    export { Modules as default } from './StyleGuide';
    
    // prefer
    import { Modules } from './StyleGuide';
    export default Modules;

  • Only import from a path in one place.

    Why? Having multiple lines that import from the same path can make code harder to maintain.

    // avoid
    import React from 'react';
    // … some other imports … //
    import { Component, PureComponent } from 'react';
    
    // prefer
    import React, { Component, PureComponent } from 'react';
    
    // prefer
    import React, {
      Component,
      PureComponent,
    } from 'react';

  • Do not export mutable bindings.

    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.

    // avoid
    let number = 3;
    export { number };
    
    // prefer
    const number = 3;
    export { number };

  • In modules with a single export, prefer default export over named export.

    Why? To encourage more files that only ever export one thing, which is better for readability and maintainability.

    // bad
    export function doThis() {}
    
    // good
    function doThis() {}
    export default doThis;
    
    // good
    export default function doThis() {}

  • Put all imports above non-import statements.

    Why? Since imports are hoisted, keeping them all at the top prevents surprising behavior.

    // avoid
    import { Form, Field } from 'react-native-validate-form';
    foo.init();
    import HtmlPdfViewer from 'html-pdf-viewer';
    
    // prefer
    import { Form, Field } from 'react-native-validate-form';
    import HtmlPdfViewer from 'html-pdf-viewer';
    
    foo.init();

  • Multiline imports should be indented just like multiline array and object literals.

    Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide, as do the trailing commas.

    // avoid
    import {firstName, secondName, thirdName, fourthName, fifthName} from './someComponent';
    
    // prefer
    import {
      firstName,
      secondName,
      thirdName,
      fourthName,
      fifthName,
    } from 'path';

  • Disallow Webpack loader syntax in module import statements.

    Why? Since using Webpack syntax in the imports couples the code to a module bundler. Prefer using the loader syntax in webpack.config.js.

    // avoid
    import fooSass from 'css!sass!foo.scss';
    import barCss from 'style!css!bar.css';
    
    // prefer
    import fooSass from 'foo.scss';
    import barCss from 'bar.css';

Objects

  • Use the literal syntax for object creation.

    // avoid
    const obj = new Object();
    
    // prefer
    const obj = {};

  • Use computed property names when creating objects with dynamic property names.

    Why? They allow you to define all the properties of an object in one place.

    function getKey(key) {
      return `${key}`;
    }
    
    // avoid
    const obj = {
      id: 1,
      name: 'Chocolate',
    };
    
    obj[getKey('enabled')] = true;
    
    // prefer
    const obj = {
      id: 1,
      name: 'Chocolate',
      [getKey('enabled')]: true,
    };

  • Use object method shorthand.

    // avoid
    const obj = {
      ctr: 1,
    
      addValue: function (value) {
        return obj.ctr + value;
      },
    };
    
    // prefer
    const obj = {
      ctr: 1,
    
      addValue(value) {
        return obj.ctr + value;
      },
    };

  • Use property value shorthand.

    Why? It is shorter to write and descriptive.

    const name = 'Juan Dela Cruz';
    
    // avoid
    const obj = {
      name: name,
    };
    
    // prefer
    const obj = {
      name,
    };

  • Group your shorthand properties at the beginning of your object declaration.

    Why? It’s easier to tell which properties are using the shorthand.

    const name = 'Juan Dela Cruz';
    const address = 'Metro Manila';
    
    // avoid
    const obj = {
      age: 24,
      gender: 'Male',
      name,
      yearsOfExperience: 4,
      address
    };
    
    // prefer
    const obj = {
      name,
      address
      age: 24,
      gender: 'Male',
      yearsOfExperience: 4,
    };

  • Only quote properties that are invalid identifiers.

    Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.

    // avoid
    const obj = {
      'foo': 1,
      'bar': 2,
      'invalid-id': 3
    };
    
    // prefer
    const obj = {
      foo: 1,
      bar: 2,
      'invalid-id': 3
    };

  • Do not call Object.prototype methods directly, such as hasOwnProperty, propertyIsEnumerable, and isPrototypeOf.

    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)).

    // avoid
    console.log(object.hasOwnProperty(key));
    
    // prefer
    console.log(Object.prototype.hasOwnProperty.call(object, key));
    
    // or
    const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
    console.log(has.call(object, key));

  • Prefer the object spread operator over Object.assign to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.

    // never do this
    const orig = { a: 1, b: 2 };
    const copy = Object.assign(orig, { c: 3 }); // this mutates the `orig` object
    delete copy.a; // so does this
    
    // avoid
    const orig = { a: 1, b: 2 };
    const copy = Object.assign({}, orig, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
    
    // prefer
    const orig = { a: 1, b: 2 };
    const copy = { ...orig, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

Arrays

  • Use the literal syntax for array creation.

    // avoid
    const things = new Array();
    
    // prefer
    const things = [];

  • Use Array#push instead of direct assignment to add items to an array.

    const things = [];
    
    // avoid
    things[things.length] = 'Chocolate';
    
    // prefer
    things.push('Chocolate');

  • Use array spreads ... to copy arrays.

    // avoid
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i += 1) {
      itemsCopy[i] = items[i];
    }
    
    // prefer
    const itemsCopy = [...items];

  • To convert an iterable object to an array, use spreads ... instead of Array.from.

    const bar = document.querySelectorAll('.foo');
    
    // this is good but...
    const things = Array.from(bar);
    
    // prefer this
    const things = [...bar];

  • Use Array.from for converting an array-like object to an array.

    const arrayLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
    
    // avoid
    const things = Array.prototype.slice.call(arrayLike);
    
    // prefer
    const things = Array.from(arrayLike);

  • Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array.

    // avoid
    const things = [...foo].map(bar);
    
    // prefer
    const things = Array.from(foo, bar);

  • 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 Arrow Implicit Return.

    // prefer
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    
    // prefer
    [1, 2, 3].map(x => x + 1);
    
    // avoid - no returned value means `acc` becomes undefined after the first iteration
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
      acc[index] = flatten;
    });
    
    // prefer
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
      acc[index] = flatten;
      return flatten;
    });
    
    // avoid
    inbox.filter((msg) => {
      const { subject, author } = msg;
      
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      } else {
        return false;
      }
    });
    
    // prefer
    inbox.filter((msg) => {
      const { subject, author } = msg;
    
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      }
    
      return false;
    });

  • Use line breaks after open and before close array brackets if an array has multiple lines

    // bad
    const arr = [
      [0, 1], [2, 3], [4, 5],
    ];
    
    // bad
    const objectInArray = [{
      id: 1,
    }, {
      id: 2,
    }];
    
    // bad
    const arr = [
      1, 2,
    ];
    
    // good
    const arr = [[0, 1], [2, 3], [4, 5]];
    
    const objectInArray = [
      {
        id: 1,
      },
      {
        id: 2,
      },
    ];
    
    const numberInArray = [
      1,
      2,
    ];

Destructuring

  • Use object destructuring when accessing and using multiple properties of an object.

    Why? Destructuring saves you from creating temporary references for those properties.

    // avoid
    function getData(data) {
      const name = data.name;
      const address = data.address;
    
      return `${name} lives in ${address}`;
    }
    
    // prefer
    function getData(data) {
      const { name, address } = data;
      return `${name} lives in ${address}`;
    }
    
    // prefer more
    function getData({ name, address }) {
      return `${name} lives in ${address}`;
    }

  • Use array destructuring

    const arr = [1, 2, 3, 4];
    
    // avoid
    const one = arr[0];
    const two = arr[1];
    
    // prefer
    const [one, two] = arr;

  • Use object destructuring for multiple return values, not array destructuring.

    Why? You can add new properties over time or change the order of things without breaking call sites.

    // avoid
    function processInput(input) {
      // ...
      return [one, two, three, four];
    }
    
    // the caller needs to think about the order of return data
    const [one, __, three] = processInput(input);
    
    // prefer
    function processInput(input) {
      // ...
      return { one, two, three, four };
    }
    
    // the caller selects only the data they need
    const { one, three } = processInput(input);

Strings

  • Use single quotes '' for strings.

    // avoid
    const name = "Juan Dela Cruz";
    
    // avoid - template literals should contain interpolation or newlines
    const name = `Juan Dela Cruz`;
    
    // prefer
    const name = 'Juan Dela Cruz';

  • Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.

    Why? Broken strings are painful to work with and make code less searchable.

    // avoid
    const longString = 'There is no one who loves or pursues or desires to obtain pain of itself, \
    because it is pain, but because occasionally circumstances occur in which toil and pain \
    can procure him some great pleasure.';
    
    // avoid
    const longString = 'There is no one who loves or pursues or desires to obtain pain of itself, ' +
      'because it is pain, but because occasionally circumstances occur in which toil and pain ' +
      'can procure him some great pleasure.';
    
    // prefer
    const longString = 'There is no one who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.';

  • When programmatically building up strings, use template strings instead of concatenation.

    Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.

    // avoid
    function doIt(stuff) {
      return 'Just do ' + stuff + '!';
    }
    
    // avoid
    function doIt(stuff) {
      return ['Just do ', stuff, '!'].join();
    }
    
    // avoid
    function doIt(stuff) {
      return `Just do ${ stuff }!`;
    }
    
    // prefer
    function doIt(stuff) {
      return `Just do ${stuff}!`;
    }
- Never use `eval()` on a string, it opens too many vulnerabilities.

  • Do not unnecessarily escape characters in strings.

    Why? Backslashes harm readability, thus they should only be present when necessary.

    // avoid
    const string = '\'this\' \i\s \"quoted\"';
    
    // prefer
    const string = '\'this\' is "quoted"';
    const string = `This is the '${stuff}'`;

Functions

  • Use named function expressions instead of function declarations.

    Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel).

    // avoid
    function foo() {
      // ...
    }
    
    // avoid
    const bar = function () {
      // ...
    };
    
    // prefer
    // lexical name distinguished from the variable-referenced invocation(s)
    const short = function longUniqueMoreDescriptiveFunctionName() {
      // ...
    };

  • Note: ECMA-262 defines a block as a list of statements. A function declaration is not a statement.

    // avoid
    if (truth) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // prefer
    let test;
    if (truth) {
      test = () => {
        console.log('Yup.');
      };
    }

  • Never name a parameter arguments. This will take precedence over the arguments object that is given to every function scope.

    // avoid
    function foo(name, options, arguments) {
      // ...
    }
    
    // prefer
    function foo(name, options, args) {
      // ...
    }

  • Never use arguments, opt to use rest syntax ... instead.

    Why? ... is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like arguments.

    // avoid
    function concatenateAll() {
      const args = Array.prototype.slice.call(arguments);
      return args.join('');
    }
    
    // prefer
    function concatenateAll(...args) {
      return args.join('');
    }

  • Use default parameter syntax rather than mutating function arguments.

    // seriously avoid
    function sampleFunc(opts) {
      opts = opts || {};
      // ...
    }
    
    // avoid
    function sampleFunc(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // prefer
    function sampleFunc(opts = {}) {
      // ...
    }

  • Avoid side effects with default parameters.

    Why? They are confusing to reason about.

    var b = 1;
    // avoid
    function count(a = b++) {
      console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3

  • Always put default parameters last.

    // avoid
    function sampleFunc(opts = {}, name) {
      // ...
    }
    
    // prefer
    function sampleFunc(name, opts = {}) {
      // ...
    }

  • Never use the Function constructor to create a new function.

    Why? Creating a function in this way evaluates a string similarly to eval(), which opens vulnerabilities.

    // avoid
    var add = new Function('a', 'b', 'return a + b');
    
    // avoid
    var subtract = Function('a', 'b', 'return a - b');

  • Spacing in a function signature.

    Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name.

    // avoid
    const w = function(){};
    const t = function (){};
    const f = function() {};
    
    // prefer
    const r = function () {};
    const o = function f() {};
    function l() {};

  • Never mutate parameters.

    Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.

    // avoid
    function sampleFunc(obj) {
      obj.key = 1;
    }
    
    // prefer
    function sampleFunc(obj) {
      const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    }

  • Never reassign parameters.

    Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the arguments object. It can also cause optimization issues, especially in V8.

    // avoid
    function sampleFunc1(a) {
      a = 1;
      // ...
    }
    
    function sampleFunc2(a) {
      if (!a) { a = 1; }
      // ...
    }
    
    // prefer
    function sampleFunc3(a) {
      const b = a || 1;
      // ...
    }
    
    function sampleFunc4(a = 1) {
      // ...
    }   

  • Prefer the use of the spread operator ... to call variadic functions.

    Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose new with apply.

    // avoid
    const x = [1, 2, 3, 4, 5];
    console.log.apply(console, x);
    
    // prefer
    const x = [1, 2, 3, 4, 5];
    console.log(...x);
    
    // avoid
    new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
    
    // prefer
    new Date(...[2016, 8, 5]);

  • 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.

    // avoid
    function foo(bar,
                 baz,
                 quux) {
      // ...
    }
    
    // prefer
    function foo(
      bar,
      baz,
      quux,
    ) {
      // ...
    }
    
    // avoid
    console.log(foo,
      bar,
      baz);
    
    // prefer
    console.log(
      foo,
      bar,
      baz,
    );

Arrow Functions

  • When you must use an anonymous function (as when passing an inline callback), use arrow function notation.

    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.

    Why not? If you have a fairly complicated function, you might move that logic out into its own named function expression.

    // avoid
    [1, 2, 3].map(function (x) {
      const y = 1;
      return x * y;
    });
    
    // prefer
    [1, 2, 3].map((x) => {
      const y = 1;
      return x * y;
    });

  • If the function body consists of a single statement returning an expression without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a return statement.

    Why? Syntactic sugar. It reads well when multiple functions are chained together.

    // avoid
    [1, 2, 3].map(item => {
      const plusOne = item + 1;
      `No return statement: ${plusOne}.`;
    });
    
    // prefer
    [1, 2, 3].map(item => `This line automatically returns: ${item}.`);
    
    // prefer
    [1, 2, 3].map((item) => {
      const plusOne = item + 1;
      return `Multiline should have a return satement: ${plusOne}.`;
    });
    
    // prefer
    [1, 2, 3].map((item, index) => ({
      [index]: item,
    }));
    
    // No implicit return with side effects
    function foo(callback) {
      const val = callback();
      if (val === true) {
        // Do something if callback returns true
      }
    }
    
    let bool = false;
    
    // avoid
    foo(() => bool = true);
    
    // prefer
    foo(() => {
      bool = true;
    });

  • In case the expression spans over multiple lines, wrap it in parentheses for better readability.

    Why? It shows clearly where the function starts and ends.

    // avoid
    [1, 2, 3].map(number => myObject.prototype.hasOwnProperty.call(
        magic,
        number,
      )
    );
    
    // prefer
    [1, 2, 3].map(number => (
      myObject.prototype.hasOwnProperty.call(
        magic,
        number,
      )
    ));

  • If your function takes a single argument and doesn’t use braces, omit the parentheses. Otherwise, always include parentheses around arguments for clarity and consistency. Note: it is also acceptable to always use parentheses.

    Why? Less visual clutter.

    // avoid
    [1, 2, 3].map((number) => number + 1);
    
    // prefer
    [1, 2, 3].map(number => number + 1);
    
    // prefer - if the line is too long do not include it in the same line as `.map()`
    [1, 2, 3].map(number => (
      `There is no one who loves or pursues or desires to obtain pain of itself. Here's the number though: ${number}.`
    ));
    
    // avoid
    [1, 2, 3].map(x => {
      const y = 1;
      return x * y;
    });
    
    // prefer
    [1, 2, 3].map((x) => {
      const y = 1;
      return x * y;
    });

  • Avoid confusing arrow function syntax (=>) with comparison operators (<=, >=).

    // avoid
    const numberValue = number => number.value >= 101 ? 'over' : 'under';
    
    // avoid
    const numberValue = (number) => number.value >= 101 ? 'over' : 'under';
    
    // prefer
    const numberValue = number => (number.value >= 101 ? 'over' : 'under');
    
    // prefer
    const numberValue = (number) => {
      const { value } = number;
      return value >= 101 ? 'over' : 'under';
    };

  • Enforce the location of arrow function bodies with implicit returns.

    // avoid these
    (foo) =>
      bar;
    
    (foo) =>
      (bar);
    
    // prefer these
    (foo) => bar;
    (foo) => (bar);
    (foo) => (
      bar
    )

Classes & Constructors

  • Always use class. Avoid manipulating prototype directly.

    Why? class syntax is more concise and easier to reason about.

    // avoid
    function Sample(arr = []) {
      this.sample = [...arr];
    }
    Sample.prototype.pop = function () {
      const value = this.sample[0];
      this.sample.splice(0, 1);
      return value;
    };
    
    // prefer
    class Sample {
      constructor(arr = []) {
        this.sample = [...arr];
      }
      pop() {
        const value = this.sample[0];
        this.sample.splice(0, 1);
        return value;
      }
    }

  • Use extends for inheritance.

    Why? It is a built-in way to inherit prototype functionality without breaking instanceof.

    // avoid
    const inherits = require('inherits');
    function Sample(items) {
      List.apply(this, items);
    }
    inherits(Sample, List);
    Sample.prototype.peek = function () {
      return this.list[0];
    };
    
    // prefer
    class Sample extends List {
      peek() {
        return this.list[0];
      }
    }

  • Methods can return this to help with method chaining.

    // avoid
    Pirate.prototype.eat = function () {
      this.jumping = true;
      return true;
    };
    
    Pirate.prototype.setCrewName = function (name) {
      this.name = name;
    };
    
    const luffy = new Pirate();
    luffy.eat(); // => true
    luffy.setCrewName('strawhats'); // => undefined
    
    // prefer
    class Pirate {
      eat() {
        this.eating = true;
        return this;
      }
    
      setCrewName(name) {
        this.name = name;
        return this;
      }
    }
    
    const luffy = new Pirate();
    
    luffy.eat()
      .setCrewName('strawhats');

  • It’s okay to write a custom toString() method, just make sure it works successfully and causes no side effects.

    class Pirate {
      constructor(options = {}) {
        this.powers = options.powers || 'muggle';
      }
    
      getDevilFruit() {
        return this.powers;
      }
    
      toString() {
        return `Pirate - ${this.getDevilFruit()}`;
      }
    }

  • 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.

    // avoid
    class Pirate {
      constructor() {}
    
      getDevilFruit() {
        return this.powers;
      }
    }
    
    // avoid
    class Luffy extends Pirate {
      constructor(...args) {
        super(...args);
      }
    }
    
    // prefer
    class Luffy extends Pirate {
      constructor(...args) {
        super(...args);
        this.powers = 'Gomu Gomu';
      }
    }

  • Avoid duplicate class members.

    Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug.

    // avoid
    class Pirate {
      one() { return 1; }
      one() { return 2; }
    }
    
    // prefer
    class Pirate {
      one() { return 1; }
      piece() { return 2; }
    }

Iterators and Generators

  • Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like for-in or for-of.

    Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.

    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.

    const numbers = [1, 2, 3, 4, 5];
    
    // avoid
    let sum = 0;
    for (let num of numbers) {
      sum += num;
    }
    sum === 15;
    
    // prefer
    let sum = 0;
    numbers.forEach((num) => {
      sum += num;
    });
    sum === 15;
    
    // best (use the functional force)
    const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 15;
    
    // avoid
    const increasedByOne = [];
    for (let i = 0; i < numbers.length; i++) {
      increasedByOne.push(numbers[i] + 1);
    }
    
    // prefer
    const increasedByOne = [];
    numbers.forEach((num) => {
      increasedByOne.push(num + 1);
    });
    
    // best (keeping it functional)
    const increasedByOne = numbers.map(num => num + 1);

  • Don’t use generators for now.

    Why? They don’t transpile well to ES5.

  • If you must use generators, make sure their function signature is spaced properly.

    Why? function and * are part of the same conceptual keyword - * is not a modifier for function, function* is a unique construct, different from function.

    // avoid
    function * foo() {
      // ...
    }
    
    // avoid
    const bar = function * () {
      // ...
    };
    
    // avoid
    const baz = function *() {
      // ...
    };
    
    // avoid
    const quux = function*() {
      // ...
    };
    
    // avoid
    function*foo() {
      // ...
    }
    
    // avoid
    function *foo() {
      // ...
    }
    
    // nope
    function
    *
    foo() {
      // ...
    }
    
    // nope
    const wat = function
    *
    () {
      // ...
    };
    
    // prefer
    function* foo() {
      // ...
    }
    
    // prefer
    const foo = function* () {
      // ...
    };

About

A collection of JavaScript / React / React Native style guide

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published