Skip to content

Latest commit

 

History

History
793 lines (597 loc) · 19.8 KB

README.md

File metadata and controls

793 lines (597 loc) · 19.8 KB

PublicisSapient Javascript Styleguide

A Javascript styleguide written from an enterprise perspective.

Table of Contents

Accessors

MDN web docs:

ESLint rules:

Accessors are more commonly referred to as getters and setters. The get and set syntax binds an object property to a function that will be called when that property is looked up or assigned a value.

This pattern looks promising but has too many drawbacks to be used on a large project. Spelling mistakes will not trigger errors with set calls. This can be avoided by using normal functions instead of getters and setters.

  // bad
  class User {
    constructor() {
      this.firstName = 'Ada';
      this.lastName = 'Lovelace';
    }

    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }

    set fullName(value) {
      const names = value.split(' ');
      this.firstName = names[0];
      this.lastName = names[1];
    }
  }

  var user = new User();

  // An error will be thrown for spelling mistakes on getters
  // TypeError: undefined is not a function
  console.log(user.fulName);

  // However since an object property can be named anything it will not be
  // thrown on setters
  user.fulName = 'Edith Clarke';

  // good
  class User {
    constructor() {
      this.firstName = 'Valerie';
      this.lastName = 'Taylor';
    }

    getFullName() {
      return `${this.firstName} ${this.lastName}`;
    }

    setFullName(value) {
      const names = value.split(' ');
      this.firstName = names[0];
      this.lastName = names[1];
    }
  }

  var user = new User();

  // An error will be thrown for spelling mistakes on getters
  // TypeError: undefined is not a function
  user.getFulName();

  // An error will also be thrown here as well
  user.setFulName('Margaret Hamilton');

  // And it's more obvious that you're calling a function like this
  user.setFullName('Radia Perlman');

Arrays

MDN web docs:

ESLint rules:

Create arrays using the literal notation instead of the constructor.

  // bad
  const planets = new Array();

  // good
  const planets = [];

Don't use spaces after open and before close brackets when creating an array.

  // bad
  const planets = [ 'Mercury', 'Venus', 'Earth' ];

  // good
  const planets = ['Mercury', 'Venus', 'Earth'];

If there are more than 5 elements in an array use line breaks to put each value on their own line.

  // bad
  const planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn'];

  // bad
  const planets = ['Mercury', 'Venus', 'Earth',
    'Mars',
    'Jupiter',
    'Saturn'
  ];

  // good
  const planets = [
    'Mercury',
    'Venus',
    'Earth',
    'Mars',
    'Jupiter',
    'Saturn'
  ];

  // good
  const planets = [
    {
      name: 'Mercury',
      color: 'red'
    },
    {
      name: 'Venus',
      color: 'blue'
    },
    {
      name: 'Mars',
      color: 'red'
    },
    {
      name: 'Earth',
      color: 'blue'
    },
    {
      name: 'Jupiter',
      color: 'brown'
    },
    {
      name: 'Saturn',
      color: 'orange'
    }
  ];

Don't use sparse arrays which have "holes" where commas are not preceded by elements. It does not apply to a trailing comma following the last element.

  //bad
  var items = [,];
  var planets = [ 'Mercury',, 'Venus' ];

  //good
  var items = [];
  var items = new Array(23);

  // trailing comma (after the last element) is not a problem
  var planets = [ 'Mercury', 'Venus', ];

Get the value of an item in an Array by using the index.

  const venus = planets[1];
  const earth = planets[2];

Loop through Arrays using the forEach method

  planets.forEach((planet, index) => {
    console.log(planet.name, index);
  });

Add items to an Array using the push method.

  planets.push({
    name: 'Mercury',
    color: 'red'
  });

Combine two or more Arrays using ... notation.

  const array1 = [1, 2, 3];
  const array2 = [4, 5, 6];

  const combinedArray = [...array1, ...array2];
  // expected output: [1, 2, 3, 4, 5, 6]

Create an Array from any array-like iterable using from. Array-like iterables include things like: String, Set, Map, and arguments.

  // create an array from a String
  const letters = Array.from('mars');
  // expected output: ['m', 'a', 'r', 's']

Use a return statement with Array methods every, filter, find, findIndex, map, reduce, reduceRight, some, sort.

  const numbers = [1, 2, 3];

  // check if all the items in the Array are less than 10 with .every()
  const allLessThanTen = numbers.every(element => {
    return element < 10;
  });
  // expected output: true

  // Create a new Array with all the items in the Array that are greater than
  // 1 with .filter()
  const greaterThanOne = numbers.filter(element => {
    return element > 1;
  });
  // expected output: [2, 3]

  // find the first item greater than 1 with .find()
  const first = numbers.find(element => {
    return element > 1;
  });
  // expected output: 2

  // find the index of the first item greater than 1 with .findIndex()
  const firstIndex = numbers.findIndex(element => {
    return element > 1;
  });
  // expected output: 1

  // Create a new Array with double all the numbers in the Array with .map()
  const doubleNumbers = numbers.map(element => {
    return element * 2;
  });
  // expected output: [2, 4, 6]

  // calculate the sum of all items using .reduce()
  const sum = numbers.reduce((accumulator, element) => {
    return accumulator + element
  });
  // expected output: 6

  // check if at least one item in the Array is less than 10 with .some()
  const someLessThanTen = numbers.some(element => {
    return element < 10;
  });
  // expected output: true

  // sort a list of words by their length with .sort()
  const sortedWords = ['Gladys', 'Mae', 'West'].sort((a, b) => {
    // if returned value is less than 0 then 'a' is sorted before 'b'
    // otherwise 'b' is sorted before 'a'
      return a.length - b.length;
  });
  // expected output: ['Mae', 'West', 'Gladys']

Strings

MDN Web Docs:

ESLint Rules:

Use single quotes '' for strings.

  // bad
  const name = "Ada Lovelace";

  // good
  const name = 'Grace Hopper';

  // good - this applies to strings in other places like import and require statements as well
  import myModule from './myModule';
  const myOtherModule = require('./myOtherModule');

Really long strings should be written as a single line and leave any line wrapping to the IDE or code editor.

// bad
  const melbaRoyMouton = 'Melba Roy Mouton (1929-1990) was an African-American \
  woman who served as Assistant Chief of Research Programs at NASA\'s Trajectory \
  and Geodynamics Division in the 1960s and headed a group of NASA \
  mathematicians called "computers". Starting as a mathematician, she was head \
  mathematician for Echo Satellites 1 and 2, and she worked up to being a Head \
  Computer Programmer and then Program Production Section Chief at Goddard Space \
  Flight Center.';

  // bad
  const dorothyVaughan = 'Dorothy Johnson Vaughan (September 20, 1910 – November 10, 2008) ' +
  'was an African American mathematician and human computer who worked for the National ' +
  'Advisory Committee for Aeronautics (NACA), and NASA, at Langley Research Center in ' +
  'Hampton, Virginia. In 1949, she became acting supervisor of the West Area Computers, ' +
  'the first African-American woman to supervise a group of staff at the center.';

  // good
  const katherineJohnson = 'Katherine Coleman Goble Johnson (born August 26, 1918) is an American mathematician whose calculations of orbital mechanics as a NASA employee were critical to the success of the first and subsequent U.S. crewed spaceflights. During her 35-year career at NASA and its predecessor, she earned a reputation for mastering complex manual calculations and helped pioneer the use of computers to perform the tasks. The space agency noted her "historical role as one of the first African-American women to work as a NASA scientist".';

Use template strings instead of concatenation when building up strings programatically.

  const astronaut = 'Ellen Ochoa';

  // bad - don't use string literals templates if you don't have any embedded expressions
  const astronaut = `Ellen Ochoa`;

  // bad
  const goal = 'When I grow up I want to be an astronaut like ' + astronaut + '!';

  // bad
  const goal = ['When I grow up', 'I want to be', 'an astronaut', 'like', astronaut, '!'].join(' ');

  // good
  const goal = `When I grow up I want to be an astronaut like ${astronaut}!`;

Do not use template string notation inside of regular strings. The output is not technically wrong however it is more than likely an unintentional mistake.

  const astronaut = 'Jenni Sydey';

  // bad
  const goal = 'When I grow up I want to be like ${astronaut}!';
  // expected output: 'When I grow up I want to be like ${astronaut}!'

  // good
  const goal = `When I grow up I want to be like ${astronaut}!`;
  // expectec output: 'When I grow up I want to be like Jenni Sydey!'

Only escape characters in strings that actually require escaping.

  // bad - the double quotes don't need escaping
  const anneMcClain = 'Anne\'s \call sign is \"Annimal\"';

  // good
  const anneMcClain = 'Anne\'s call sign is "Annimal"';

Don't use spacing between curly brackets.

  //bad 
  const goal = `When I grow up I want to be like ${ astronaut}!`
  const goal = `When I grow up I want to be like ${astronaut }!`
  const goal = `When I grow up I want to be like ${ astronaut }!`

  //good
  const goal = `When I grow up I want to be like ${astronaut}!`
  const goal = `When I grow up I want to be like ${
    astronaut
  }!`

Global

ESLint Rules:

Never use eval() in any circumstance. It is an enormous security risk.

  // bad
  console.log(eval('2 + 2'));
  // expected output: 4

  // bad
  console.log(eval(new String('2 + 2')));
  // expected output: 2 + 2

  // bad
  console.log(eval('2 + 2') === eval('4'));
  // expected output: true

When importing multiple members of a module, ensure they are not on different lines

// bad
  import {merge} from 'module';
  import something from 'another-module';
  import {find} from 'module';

  //good
  import {merge, find} from 'module';
  import something from 'another-module';

  // not mergeable 
  import {merge} from 'module';
  import * as something from 'module';

Whitespace

ESLint rules:

SonarQube rules:

Use a space before the opening brace of a block.

  // bad
  function test(){
    console.log('test');
  }

  // good
  function test() {
    console.log('test');
  }

Do not use a space between the argument list and the function name in function calls and declarations.

  // bad
  function marco () {
    console.log ('polo');
  }

  // good
  function marco() {
    console.log('polo');
  }

Use a space before opening parenthesis in control statements (if, while, etc.).

  // bad
  if(condition) {...}

  // good
  if (condition) {...}

Use spaces between operators.

  // bad
  const a=b+10;

  // good
  const a = b + 10;

Use a space after commas.

  // bad
  const list = [1,2,3,4]
  function fullName (firstName,lastName) {...}

  // good
  const list = [1, 2, 3, 4]
  function fullName (firstName, lastName) {...}

Use spaces between arrow functions arrow.

  //bad
  ()=> {};
  () =>{};
  a =>a;
  a=> a;

  //good
  () => {};
  a => a;

Do not use trailing spaces at the end of code or on blank lines.

  // bad
  const list = [1, 2, 3, 4];//•••••
  function fullName (firstName, lastName) {...};//••
  //•••••

  //good
  const list = [1, 2, 3, 4];
  function fullName (firstName, lastName) {...};

Do not use multiple spaces

  //bad
  if (condition) {...}
  const list = [1, 2, 3,  4];
  const a = b +  10;
  a ?  b: c

  //good
  if (condition) {...}
  const list = [1, 2, 3, 4];
  const a = b + 10;
  a ? b: c

Do not leave multiple blank lines.

  // bad
  const value = 'hello world';


  console.log(value);

  // good
  const value = 'hello world';
  console.log(value);

Leave a blank line between a block and the next statement.

  // bad
  if (valid) {
    return value;
  }
  return message;

  // good
  if (valid) {
    return value;
  }

  return message;
  // bad
  const dog = {
    run() {
    },
    bark() {
    },
  };
  return obj;

  // good
  const dog = {
    run() {
    },

    bark() {
    },
  };

  return obj;

Each line of code should be no more than 100 characters. This helps with readability in an IDE as well as in pull requests. This includes all lines including strings, template literals, and comments.

  // bad
  const amazingPeople = {mathematicians = ['Katherine Johnson', 'Dorothy Vaughan'], scientists: ['Alice Ball', 'Mary Elliott Hill'], astronauts: ['Mae C. Jemison', 'Stephanie Wilson']};

  // good
  const amazingPeople = {
    mathematicians = ['Katherine Johnson', 'Dorothy Vaughan'],
    scientists: ['Alice Ball', 'Mary Elliott Hill'],
    astronauts: ['Mae C. Jemison', 'Stephanie Wilson']
  };

  // bad
  // This is a really long comment that should really be written in multiple lines but instead has been written in a single line.

  // good
  // This is a really long comment that has been written on multiple lines instead of on a single
  // line so that it is easier to read.

  // bad
  const str = `this is a really long string that should really written over multiple lines but instead has been written in a single line`;

  // good
  const str = `this is a really long string that has been written over multiple lines instead
  of being written in a single line`;

Each function should have no more than 200 lines to reduce complexity and increase testability. This is especially important in React components where the render functions can quickly get out of hand trying to do too much. This includes all comments and whitespace but ignores IIFEs (Immediately Invoked Function Expressions) to match SonarQube functionality.

Comparison Operators & Equality

MDN web docs:

ESLint rules:

Use === and !== over == and !=

Use shortcuts for booleans, but explicit comparisons for strings and numbers

  // bad
  if (isLoaded === true) {...}

  // good
  if (isLoaded) {...}

  // bad
  if (value) {...}

  // good
  if (value !== '') {...}

  // bad
  if (items.length) {...}

  // good
  if (items.length > 0) {...}

Do not nest ternary expressions

  // bad
  const thing = foo ? bar : baz === qux ? quxx : foobar;
  // good
  const thing = foo ? bar : foobar;

  // good
  let thing;

  if (foo) {
    thing = bar;
  } else if (baz === qux) {
    thing = quxx;
  } else {
    thing = foobar;
  }

Do not use ternary expressions when simpler alternatives exist

  // bad
  const isEven = (value % 2 === 0) ? true : false;

  // good
  const isEven = value % 2 === 0;


  // bad
  const isOdd = (value % 2 === 0) ? false : true;

  // good
  const isOdd = value % 2 !== 0;
  // bad
  const value = newNumber ? newNumber : 1;

  // good
  const value = newNumber || 1;

Control Statements

MDN web docs:

ESLint rules:

SonarQube rules:

Use curly braces {...} for all block statements including if, else, for, while, and do. These braces are technically optional when there is only one line however omitting them leads to inconsistent code that is prone to errors in the future.

  // bad
  if (error) return false;

  // bad
  if (error)
    return false;

  // good
  if (error) {
    return false;
  }

Do not use yoda conditions. A yoda condition is where the literal value is before the variable, similar to how yoda would speak.

  // bad
  if ('red' === color) {
    // ...
  }
  if (5 > count) {
    // ...
  }
  if (0 <= x && x < 1) {
    // ...
  }

  // good
  if (color === 'red') {
    // ...
  }
  if (count < 5) {
    // ...
  }
  if (x >= 0 && x < 1) {
    // ...
  }