Skip to content

Latest commit

 

History

History
executable file
·
788 lines (586 loc) · 17.7 KB

STYLE-GUIDE.md

File metadata and controls

executable file
·
788 lines (586 loc) · 17.7 KB

Indentation

When writing any block of code that is logically subordinate to the line immediately before and after it, that block should be indented two spaces more than the surrounding lines

  • Do not put any tab characters anywhere in your code. You would do best to set your development environment to compose tabs with spaces or to stop pressing the tab key entirely
  • Increase the indent level for all blocks by two extra spaces
    • When a line opens a block, the next line starts 2 spaces further in than the line that opened

      // good:
      if(condition){
        action();
      }
      
      // bad:
      if(condition){
      action();
      }
    • When a line closes a block, that line starts at the same level as the line that opened the block

      // good:
      if(condition){
        action();
      }
      
      // bad:
      if(condition){
        action();
        }
    • No two lines should ever have more or less than 2 spaces difference in their indentation. Any number of mistakes in the above rules could lead to this, but one example would be:

      // bad:
      transmogrify({
        a: {
          b: function(){
          }
      }});
    • use sublime's arrow collapsing as a guide. do the collapsing lines seem like they should be 'contained' by the line with an arrow on it?

Variable names

  • A single descriptive word is best.

    // good:
    var animals = ['cat', 'dog', 'fish'];
    
    // bad:
    var targetInputs = ['cat', 'dog', 'fish'];
  • Collections such as arrays and maps should have plural noun variable names.

    // good:
    var animals = ['cat', 'dog', 'fish'];
    
    // bad:
    var animalList = ['cat', 'dog', 'fish'];
    
    // bad:
    var animal = ['cat', 'dog', 'fish'];
  • Name your variables after their purpose, not their structure

    // good:
    var animals = ['cat', 'dog', 'fish'];
    
    // bad:
    var array = ['cat', 'dog', 'fish'];

Language constructs

  • Do not use for...in statements with the intent of iterating over a list of numeric keys. Use a for-with-semicolons statement in stead.

    // good:
    var list = ['a', 'b', 'c']
    for(var i = 0; i < list.length; i++){
      alert(list[i]);
    }
    
    // bad:
    var list = ['a', 'b', 'c']
    for(var i in list){
      alert(list[i]);
    }
  • Never omit braces for statement blocks (although they are technically optional).

    // good:
    for(key in object){
      alert(key);
    }
    
    // bad:
    for(key in object)
      alert(key);
  • Always use === and !==, since == and != will automatically convert types in ways you're unlikely to expect.

    // good:
    
    // this comparison evaluates to false, because the number zero is not the same as the empty string.
    if(0 === ''){
      alert('looks like they\'re equal');
    }
    
    // bad:
    
    // This comparison evaluates to true, because after type coercion, zero and the empty string are equal.
    if(0 == ''){
      alert('looks like they\'re equal');
    }

Semicolons

  • Don't forget semicolons at the end of lines

    // good:
    alert('hi');
    
    // bad:
    alert('hi')
  • Semicolons are not required at the end of statements that include a block--i.e. if, for, while, etc.

    // good:
    if(condition){
      response();
    }
    
    // bad:
    if(condition){
      response();
    };
  • Misleadingly, a function may be used at the end of a normal assignment statement, and would require a semicolon (even though it looks rather like the end of some statement block).

    // good:
    var greet = function(){
      alert('hi');
    };
    
    // bad:
    var greet = function(){
      alert('hi');
    }

Supplemental reading

Code density

  • Conserve line quantity by minimizing the number lines you write in. The more concisely your code is written, the more context can be seen in one screen.
  • Conserve line length by minimizing the amount of complexity you put on each line. Long lines are difficult to read. Rather than a character count limit, I recommend limiting the amount of complexity you put on a single line. Try to make it easily read in one glance. This goal is in conflict with the line quantity goal, so you must do your best to balance them.

Comments

  • Provide comments any time you are confident it will make reading your code easier.
  • Be aware that comments come at some cost. They make a file longer and can drift out of sync with the code they annotate.
  • Comment on what code is attempting to do, not how it will achieve it.
  • A good comment is often less effective than a good variable name.

Padding & additional whitespace

  • Generally, we don't care where you put extra spaces, provided they are not distracting.

  • You may use it as padding for visual clarity. If you do though, make sure it's balanced on both sides.

    // optional:
    alert( "I chose to put visual padding around this string" );
    
    // bad:
    alert( "I only put visual padding on one side of this string");
  • You may use it to align two similar lines, but it is not recommended. This pattern usually leads to unnecessary edits of many lines in your code every time you change a variable name.

    // discouraged:
    var firstItem  = getFirst ();
    var secondItem = getSecond();
  • Put else and else if statements on the same line as the ending curly brace for the preceding if block

    // good:
    if(condition){
      response();
    }else{
      otherResponse();
    }
    
    // bad:
    if(condition){
      response();
    }
    else{
      otherResponse();
    }

Working with files

  • Do not end a file with any character other than a newline.

Opening or closing too many blocks at once

  • The more blocks you open on a single line, the more your reader needs to remember about the context of what they are reading. Try to resolve your blocks early, and refactor. A good rule is to avoid closing more than two blocks on a single line--three in a pinch.

    // avoid:
    _.ajax(url, {success: function(){
      // ...
    }});
    
    // prefer:
    _.ajax(url, {
      success: function(){
        // ...
      }
    });

Variable declaration

Capital letters in variable names

  • Some people choose to use capitalization of the first letter in their variable names to indicate that they contain a [class](http://en.wikipedia.org/wiki/Class_(computer_science\)). This capitalized variable might contain a function, a prototype, or some other construct that acts as a representative for the whole class.
  • Optionally, some people use a capital letter only on functions that are written to be run with the keyword new.

Minutia

  • Don't rely on JavaScripts implicit global variables. If you are intending to write to the global scope, export things to window.* explicitly instead.

    // good:
    var overwriteNumber = function(){
      window.exported = Math.random();
    };
    
    // bad:
    var overwriteNumber = function(){
      exported = Math.random();
    };
  • For lists, put commas at the end of each newline, not at the beginning of each item in a list

    // good:
    var animals = [
      'ape',
      'bat',
      'cat'
    ];
    
    // bad:
    var animals = [
        'ape'
      , 'bat'
      , 'cat'
    ];
  • Avoid use of switch statements altogether. They are hard to outdent using the standard whitespace rules above, and are prone to error due to missing break statements. See this article for more detail.

  • Prefer single quotes around JavaScript strings, rather than double quotes. Having a standard of any sort is preferable to a mix-and-match approach, and single quotes allow for easy embedding of HTML, which prefers double quotes around tag attributes.

    // good:
    var dog = 'dog';
    var cat = 'cat';
    
    // acceptable:
    var dog = "dog";
    var cat = "cat";
    
    // bad:
    var dog = 'dog';
    var cat = "cat";

HTML

  • Do not use ids for html elements. Use a class instead.

    <!-- good -->
    <img class="lucy" />
    
    <!-- bad -->
    <img id="lucy" />
  • Do not include a type=text/javascript" attribute on script tags

    <!-- good -->
    <script src="a.js"></script>
    
    <!-- bad -->
    <script src="a.js" type="text/javascript"></script>
  • Only quote properties that are invalid identifiers.

    // bad
    const bad = {
      'foo': 3,
      'bar': 4,
      'data-blah': 5,
    };
    
    // good
    const good = {
      foo: 3,
      bar: 4,
      'data-blah': 5,
    };
  • To convert an array-like object to an array, use Array#from.

    const foo = document.querySelectorAll('.foo');
    const nodes = Array.from(foo);
  • 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.

    // bad
    function sayHi(name) {
      return 'How are you, ' + name + '?';
    }
    
    // bad
    function sayHi(name) {
      return ['How are you, ', name, '?'].join();
    }
    
    // good
    function sayHi(name) {
      return `How are you, ${name}?`;
    }
  • 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 Array-like like arguments.

    // bad
    function concatenateAll() {
      const args = Array.prototype.slice.call(arguments);
      return args.join('');
    }
    
    // good
    function concatenateAll(...args) {
      return args.join('');
    }
  • Use default parameter syntax rather than mutating function arguments.

    // really bad
    function handleThings(opts) {
      // No! We shouldn't mutate function arguments.
      // Double bad: if opts is falsy it'll be set to an object which may
      // be what you want but it can introduce subtle bugs.
      opts = opts || {};
      // ...
    }
    
    // good
    function handleThings(opts = {}) {
      // ...
    }
  • 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.

    // bad
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // good
    const x = function () {};
    const y = function a() {};
  • When you must use function expressions (as when passing an anonymous function), 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 function declaration.

    // bad
    [1, 2, 3].map(function (x) {
      const y = x + 1;
      return x * y;
    });
    
    // good
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
  • 13.4 Assign variables where you need them, but place them in a reasonable place.

    Why? let and const are block scoped and not function scoped.

    // bad - unnecessary function call
    function checkName(hasName) {
      const name = getName();
    
      if (hasName === 'test') {
        return false;
      }
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }
    
    // good
    function checkName(hasName) {
      if (hasName === 'test') {
        return false;
      }
    
      const name = getName();
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }
  • Use shortcuts.

    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
  • Use braces with all multi-line blocks.

    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function foo() { return false; }
    
    // good
    function bar() {
      return false;
    }
  • If you're using multi-line blocks with if and else, put else on the same line as your if block's closing brace.

    // bad
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // good
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }
  • Use /** ... */ for multi-line comments. Include a description, specify types and values for all parameters and return values.

    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param {String} tag
     * @return {Element} element
     */
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
  • Prefixing your comments with FIXME or TODO helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are FIXME -- need to figure this out or TODO -- need to implement.

    Use // FIXME: to annotate problems.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // FIXME: shouldn't use a global here
        total = 0;
      }
    }

    Use // TODO: to annotate solutions to problems.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // TODO: total should be configurable by an options param
        this.total = 0;
      }
    }
  • Add spaces inside curly braces.

    // bad
    const foo = {clark: 'kent'};
    
    // good
    const foo = { clark: 'kent' };
  • Do not add spaces inside parentheses.

    // bad
    function bar( foo ) {
      return foo;
    }
    
    // good
    function bar(foo) {
      return foo;
    }
  • Additional trailing comma: Yup.

    Why? This reduces mental overhead when manually editing lists and leads to cleaner git diffs.

    // bad - git diff without trailing comma
    const hero = {
         firstName: 'Florence',
    -    lastName: 'Nightingale'
    +    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb graph', 'modern nursing']
    };
    
    // good - git diff with trailing comma
    const hero = {
         firstName: 'Florence',
         lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing'],
    };
    
    // bad
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully'
    };
    
    const heroes = [
      'Batman',
      'Superman'
    ];
    
    // good
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully',
    };
    
    const heroes = [
      'Batman',
      'Superman',
    ];
  • Type Casting & Coercion

    Prefer explicit coercion

    Strings:

    let reviewScore = 9;
    
    // bad
    const totalScore = reviewScore + '';
    
    // good
    const totalScore = String(this.reviewScore);
    
    > Numbers: Use `Number` or `ParseInt`
    
    ```javascript
    const inputValue = '4';
    
    // good
    const val = Number(inputValue);
    
    // good
    const binary = parseInt(inputValue, 2);
    
    > Booleans:
    
    ```javascript
    const age = 0;
    
    // good
    const hasAge = Boolean(age);
    
    // good
    const hasAge = !!age;
  • Use a leading underscore _ when naming private properties.

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';

Events

  • When attaching data payloads to events, pass an object 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:

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', (e, listingId) => {
      // do something with listingId
    });

    prefer:

    // good
    $(this).trigger('listingUpdated', { listingId: listing.id });
    
    ...
    
    $(this).on('listingUpdated', (e, data) => {
      // do something with data.listingId
    });

jQuery

  • Prefix jQuery object variables with a $.

    // bad
    const sidebar = $('.sidebar');
    
    // good
    const $sidebar = $('.sidebar');
    
    // good
    const $sidebarBtn = $('.sidebar-btn');