Git Product home page Git Product logo

idiomatic.js's Introduction

Principles of Writing Consistent, Idiomatic JavaScript

This is a living document and new ideas for improving the code around us are always welcome. Contribute: fork, clone, branch, commit, push, pull request.

All code in any code-base should look like a single person typed it, no matter how many people contributed.

The following list outlines the practices that I use in all code that I am the original author of; contributions to projects that I have created should follow these guidelines.

I do not intend to impose my style preferences on other people's code or projects; if an existing common style exists, it should be respected.

"Arguments over style are pointless. There should be a style guide, and you should follow it"

Rebecca Murphey

 

"Part of being a good steward to a successful project is realizing that writing code for yourself is a Bad Idea™. If thousands of people are using your code, then write your code for maximum clarity, not your personal preference of how to get clever within the spec."

Idan Gazit

Translations

Important, Non-Idiomatic Stuff:

Code Quality Tools, Resources & References

Get Smart

The following should be considered 1) incomplete, and 2) REQUIRED READING. I don't always agree with the style written by the authors below, but one thing is certain: They are consistent. Furthermore, these are authorities on the language.

Build & Deployment Process

Projects should always attempt to include some generic means by which source can be linted, tested and compressed in preparation for production use. For this task, grunt by Ben Alman is second to none and has officially replaced the "kits/" directory of this repo.

Test Facility

Projects must include some form of unit, reference, implementation or functional testing. Use case demos DO NOT QUALIFY as "tests". The following is a list of test frameworks, none of which are endorsed more than the other.

Table of Contents


Preface

The following sections outline a reasonable style guide for modern JavaScript development and are not meant to be prescriptive. The most important take-away is the law of code style consistency. Whatever you choose as the style for your project should be considered law. Link to this document as a statement of your project's commitment to code style consistency, readability and maintainability.

Idiomatic Style Manifesto

  1. Whitespace
  • Never mix spaces and tabs.
  • When beginning a project, before you write any code, choose between soft indents (spaces) or real tabs, consider this law.
    • For readability, I always recommend setting your editor's indent size to two characters — this means two spaces or two spaces representing a real tab.
  • If your editor supports it, always work with the "show invisibles" setting turned on. The benefits of this practice are:
    • Enforced consistency
    • Eliminating end of line whitespace
    • Eliminating blank line whitespace
    • Commits and diffs that are easier to read
  • Use Editorconfig when possible. It supports most IDEs and handles most whitespace settings.
  1. Beautiful Syntax

    A. Parens, Braces, Linebreaks

    // if/else/for/while/try always have spaces, braces and span multiple lines
    // this encourages readability
    
    // 2.A.1.1
    // Examples of really cramped syntax
    
    if(condition) doSomething();
    
    while(condition) iterating++;
    
    for(var i=0;i<100;i++) someIterativeFn();
    
    
    // 2.A.1.1
    // Use whitespace to promote readability
    
    if ( condition ) {
      // statements
    }
    
    while ( condition ) {
      // statements
    }
    
    for ( var i = 0; i < 100; i++ ) {
      // statements
    }
    
    // Even better:
    
    var i,
      length = 100;
    
    for ( i = 0; i < length; i++ ) {
      // statements
    }
    
    // Or...
    
    var i = 0,
      length = 100;
    
    for ( ; i < length; i++ ) {
      // statements
    }
    
    var prop;
    
    for ( prop in object ) {
      // statements
    }
    
    
    if ( true ) {
      // statements
    } else {
      // statements
    }

    B. Assignments, Declarations, Functions ( Named, Expression, Constructor )

    // 2.B.1.1
    // Variables
    var foo = "bar",
      num = 1,
      undef;
    
    // Literal notations:
    var array = [],
      object = {};
    
    
    // 2.B.1.2
    // Using only one `var` per scope (function) or one `var` for each variable,
    // promotes readability and keeps your declaration list free of clutter.
    // Using one `var` per variable you can take more control of your versions
    // and makes it easier to reorder the lines.
    // One `var` per scope makes it easier to detect undeclared variables
    // that may become implied globals.
    // Choose better for your project and never mix them.
    
    // Bad
    var foo = "",
      bar = "";
    var qux;
    
    // Good
    var foo = "";
    var bar = "";
    var qux;
    
    // or..
    var foo = "",
      bar = "",
      qux;
    
    // or..
    var // Comment on these
    foo = "",
    bar = "",
    quux;
    
    // 2.B.1.3
    // var statements should always be in the beginning of their respective scope (function).
    
    
    // Bad
    function foo() {
    
      // some statements here
    
      var bar = "",
        qux;
    }
    
    // Good
    function foo() {
      var bar = "",
        qux;
    
      // all statements after the variables declarations.
    }
    
    // 2.B.1.4
    // const and let, from ECMAScript 6, should likewise be at the top of their scope (block).
    
    // Bad
    function foo() {
      let foo,
        bar;
      if ( condition ) {
        bar = "";
        // statements
      }
    }
    // Good
    function foo() {
      let foo;
      if ( condition ) {
        let bar = "";
        // statements
      }
    }
    // 2.B.2.1
    // Named Function Declaration
    function foo( arg1, argN ) {
    
    }
    
    // Usage
    foo( arg1, argN );
    
    
    // 2.B.2.2
    // Named Function Declaration
    function square( number ) {
      return number * number;
    }
    
    // Usage
    square( 10 );
    
    // Really contrived continuation passing style
    function square( number, callback ) {
      callback( number * number );
    }
    
    square( 10, function( square ) {
      // callback statements
    });
    
    
    // 2.B.2.3
    // Function Expression
    var square = function( number ) {
      // Return something valuable and relevant
      return number * number;
    };
    
    // Function Expression with Identifier
    // This preferred form has the added value of being
    // able to call itself and have an identity in stack traces:
    var factorial = function factorial( number ) {
      if ( number < 2 ) {
        return 1;
      }
    
      return number * factorial( number - 1 );
    };
    
    
    // 2.B.2.4
    // Constructor Declaration
    function FooBar( options ) {
    
      this.options = options;
    }
    
    // Usage
    var fooBar = new FooBar({ a: "alpha" });
    
    fooBar.options;
    // { a: "alpha" }

    C. Exceptions, Slight Deviations

    // 2.C.1.1
    // Functions with callbacks
    foo(function() {
      // Note there is no extra space between the first paren
      // of the executing function call and the word "function"
    });
    
    // Function accepting an array, no space
    foo([ "alpha", "beta" ]);
    
    // 2.C.1.2
    // Function accepting an object, no space
    foo({
      a: "alpha",
      b: "beta"
    });
    
    // Single argument string literal, no space
    foo("bar");
    
    // Expression parens, no space
    if ( !("foo" in obj) ) {
      obj = (obj.bar || defaults).baz;
    }

    D. Consistency Always Wins

    In sections 2.A-2.C, the whitespace rules are set forth as a recommendation with a simpler, higher purpose: consistency. It's important to note that formatting preferences, such as "inner whitespace" should be considered optional, but only one style should exist across the entire source of your project.

    // 2.D.1.1
    
    if (condition) {
      // statements
    }
    
    while (condition) {
      // statements
    }
    
    for (var i = 0; i < 100; i++) {
      // statements
    }
    
    if (true) {
      // statements
    } else {
      // statements
    }

    E. Quotes

    Whether you prefer single or double shouldn't matter, there is no difference in how JavaScript parses them. What ABSOLUTELY MUST be enforced is consistency. Never mix quotes in the same project. Pick one style and stick with it.

    F. End of Lines and Empty Lines

    Whitespace can ruin diffs and make changesets impossible to read. Consider incorporating a pre-commit hook that removes end-of-line whitespace and blanks spaces on empty lines automatically.

  2. Type Checking (Courtesy jQuery Core Style Guidelines)

    A. Actual Types

    String:

     typeof variable === "string"
    

    Number:

     typeof variable === "number"
    

    Boolean:

     typeof variable === "boolean"
    

    Object:

     typeof variable === "object"
    

    Array:

     Array.isArray( arrayLikeObject )
     (wherever possible)
    

    Node:

     elem.nodeType === 1
    

    null:

     variable === null
    

    null or undefined:

     variable == null
    

    undefined:

    Global Variables:

     typeof variable === "undefined"
    

    Local Variables:

     variable === undefined
    

    Properties:

     object.prop === undefined
     object.hasOwnProperty( prop )
     "prop" in object
    

    B. Coerced Types

    Consider the implications of the following...

    Given this HTML:

    <input type="text" id="foo-input" value="1">
    // 3.B.1.1
    
    // `foo` has been declared with the value `0` and its type is `number`
    var foo = 0;
    
    // typeof foo;
    // "number"
    ...
    
    // Somewhere later in your code, you need to update `foo`
    // with a new value derived from an input element
    
    foo = document.getElementById("foo-input").value;
    
    // If you were to test `typeof foo` now, the result would be `string`
    // This means that if you had logic that tested `foo` like:
    
    if ( foo === 1 ) {
    
      importantTask();
    
    }
    
    // `importantTask()` would never be evaluated, even though `foo` has a value of "1"
    
    
    // 3.B.1.2
    
    // You can preempt issues by using smart coercion with unary + or - operators:
    
    foo = +document.getElementById("foo-input").value;
    //    ^ unary + operator will convert its right side operand to a number
    
    // typeof foo;
    // "number"
    
    if ( foo === 1 ) {
    
      importantTask();
    
    }
    
    // `importantTask()` will be called

    Here are some common cases along with coercions:

    // 3.B.2.1
    
    var number = 1,
      string = "1",
      bool = false;
    
    number;
    // 1
    
    number + "";
    // "1"
    
    string;
    // "1"
    
    +string;
    // 1
    
    +string++;
    // 1
    
    string;
    // 2
    
    bool;
    // false
    
    +bool;
    // 0
    
    bool + "";
    // "false"
    // 3.B.2.2
    
    var number = 1,
      string = "1",
      bool = true;
    
    string === number;
    // false
    
    string === number + "";
    // true
    
    +string === number;
    // true
    
    bool === number;
    // false
    
    +bool === number;
    // true
    
    bool === string;
    // false
    
    bool === !!string;
    // true
    // 3.B.2.3
    
    var array = [ "a", "b", "c" ];
    
    !!~array.indexOf("a");
    // true
    
    !!~array.indexOf("b");
    // true
    
    !!~array.indexOf("c");
    // true
    
    !!~array.indexOf("d");
    // false
    
    // Note that the above should be considered "unnecessarily clever"
    // Prefer the obvious approach of comparing the returned value of
    // indexOf, like:
    
    if ( array.indexOf( "a" ) >= 0 ) {
      // ...
    }
    // 3.B.2.4
    
    
    var num = 2.5;
    
    parseInt( num, 10 );
    
    // is the same as...
    
    ~~num;
    
    num >> 0;
    
    num >>> 0;
    
    // All result in 2
    
    
    // Keep in mind however, that negative numbers will be treated differently...
    
    var neg = -2.5;
    
    parseInt( neg, 10 );
    
    // is the same as...
    
    ~~neg;
    
    neg >> 0;
    
    // All result in -2
    // However...
    
    neg >>> 0;
    
    // Will result in 4294967294
    
    
    
  3. Conditional Evaluation

    // 4.1.1
    // When only evaluating that an array has length,
    // instead of this:
    if ( array.length > 0 ) ...
    
    // ...evaluate truthiness, like this:
    if ( array.length ) ...
    
    
    // 4.1.2
    // When only evaluating that an array is empty,
    // instead of this:
    if ( array.length === 0 ) ...
    
    // ...evaluate truthiness, like this:
    if ( !array.length ) ...
    
    
    // 4.1.3
    // When only evaluating that a string is not empty,
    // instead of this:
    if ( string !== "" ) ...
    
    // ...evaluate truthiness, like this:
    if ( string ) ...
    
    
    // 4.1.4
    // When only evaluating that a string _is_ empty,
    // instead of this:
    if ( string === "" ) ...
    
    // ...evaluate falsy-ness, like this:
    if ( !string ) ...
    
    
    // 4.1.5
    // When only evaluating that a reference is true,
    // instead of this:
    if ( foo === true ) ...
    
    // ...evaluate like you mean it, take advantage of built in capabilities:
    if ( foo ) ...
    
    
    // 4.1.6
    // When evaluating that a reference is false,
    // instead of this:
    if ( foo === false ) ...
    
    // ...use negation to coerce a true evaluation
    if ( !foo ) ...
    
    // ...Be careful, this will also match: 0, "", null, undefined, NaN
    // If you _MUST_ test for a boolean false, then use
    if ( foo === false ) ...
    
    
    // 4.1.7
    // When only evaluating a ref that might be null or undefined, but NOT false, "" or 0,
    // instead of this:
    if ( foo === null || foo === undefined ) ...
    
    // ...take advantage of == type coercion, like this:
    if ( foo == null ) ...
    
    // Remember, using == will match a `null` to BOTH `null` and `undefined`
    // but not `false`, "" or 0
    null == undefined

    ALWAYS evaluate for the best, most accurate result - the above is a guideline, not a dogma.

    // 4.2.1
    // Type coercion and evaluation notes
    
    // Prefer `===` over `==` (unless the case requires loose type evaluation)
    
    // === does not coerce type, which means that:
    
    "1" === 1;
    // false
    
    // == does coerce type, which means that:
    
    "1" == 1;
    // true
    
    
    // 4.2.2
    // Booleans, Truthies & Falsies
    
    // Booleans:
    true, false
    
    // Truthy:
    "foo", 1
    
    // Falsy:
    "", 0, null, undefined, NaN, void 0
  4. Practical Style

    // 5.1.1
    // A Practical Module
    
    (function( global ) {
      var Module = (function() {
    
        var data = "secret";
    
        return {
          // This is some boolean property
          bool: true,
          // Some string value
          string: "a string",
          // An array property
          array: [ 1, 2, 3, 4 ],
          // An object property
          object: {
            lang: "en-Us"
          },
          getData: function() {
            // get the current value of `data`
            return data;
          },
          setData: function( value ) {
            // set the value of `data` and return it
            return ( data = value );
          }
        };
      })();
    
      // Other things might happen here
    
      // expose our module to the global object
      global.Module = Module;
    
    })( this );
    // 5.2.1
    // A Practical Constructor
    
    (function( global ) {
    
      function Ctor( foo ) {
    
        this.foo = foo;
    
        return this;
      }
    
      Ctor.prototype.getFoo = function() {
        return this.foo;
      };
    
      Ctor.prototype.setFoo = function( val ) {
        return ( this.foo = val );
      };
    
    
      // To call constructor's without `new`, you might do this:
      var ctor = function( foo ) {
        return new Ctor( foo );
      };
    
    
      // expose our constructor to the global object
      global.ctor = ctor;
    
    })( this );
  5. Naming

    A. You are not a human code compiler/compressor, so don't try to be one.

    The following code is an example of egregious naming:

    // 6.A.1.1
    // Example of code with poor names
    
    function q(s) {
      return document.querySelectorAll(s);
    }
    var i,a=[],els=q("#foo");
    for(i=0;i<els.length;i++){a.push(els[i]);}

    Without a doubt, you've written code like this - hopefully that ends today.

    Here's the same piece of logic, but with kinder, more thoughtful naming (and a readable structure):

    // 6.A.2.1
    // Example of code with improved names
    
    function query( selector ) {
      return document.querySelectorAll( selector );
    }
    
    var idx = 0,
      elements = [],
      matches = query("#foo"),
      length = matches.length;
    
    for ( ; idx < length; idx++ ) {
      elements.push( matches[ idx ] );
    }

    A few additional naming pointers:

    // 6.A.3.1
    // Naming strings
    
    `dog` is a string
    
    
    // 6.A.3.2
    // Naming arrays
    
    `dogs` is an array of `dog` strings
    
    
    // 6.A.3.3
    // Naming functions, objects, instances, etc
    
    camelCase; function and var declarations
    
    
    // 6.A.3.4
    // Naming constructors, prototypes, etc.
    
    PascalCase; constructor function
    
    
    // 6.A.3.5
    // Naming regular expressions
    
    rDesc = //;
    
    
    // 6.A.3.6
    // From the Google Closure Library Style Guide
    
    functionNamesLikeThis;
    variableNamesLikeThis;
    ConstructorNamesLikeThis;
    EnumNamesLikeThis;
    methodNamesLikeThis;
    SYMBOLIC_CONSTANTS_LIKE_THIS;

    B. Faces of this

    Beyond the generally well known use cases of call and apply, always prefer .bind( this ) or a functional equivalent, for creating BoundFunction definitions for later invocation. Only resort to aliasing when no preferable option is available.

    // 6.B.1
    function Device( opts ) {
    
      this.value = null;
    
      // open an async stream,
      // this will be called continuously
      stream.read( opts.path, function( data ) {
    
        // Update this instance's current value
        // with the most recent value from the
        // data stream
        this.value = data;
    
      }.bind(this) );
    
      // Throttle the frequency of events emitted from
      // this Device instance
      setInterval(function() {
    
        // Emit a throttled event
        this.emit("event");
    
      }.bind(this), opts.freq || 100 );
    }
    
    // Just pretend we've inherited EventEmitter ;)

    When unavailable, functional equivalents to .bind exist in many modern JavaScript libraries.

    // 6.B.2
    
    // eg. lodash/underscore, _.bind()
    function Device( opts ) {
    
      this.value = null;
    
      stream.read( opts.path, _.bind(function( data ) {
    
        this.value = data;
    
      }, this) );
    
      setInterval(_.bind(function() {
    
        this.emit("event");
    
      }, this), opts.freq || 100 );
    }
    
    // eg. jQuery.proxy
    function Device( opts ) {
    
      this.value = null;
    
      stream.read( opts.path, jQuery.proxy(function( data ) {
    
        this.value = data;
    
      }, this) );
    
      setInterval( jQuery.proxy(function() {
    
        this.emit("event");
    
      }, this), opts.freq || 100 );
    }
    
    // eg. dojo.hitch
    function Device( opts ) {
    
      this.value = null;
    
      stream.read( opts.path, dojo.hitch( this, function( data ) {
    
        this.value = data;
    
      }) );
    
      setInterval( dojo.hitch( this, function() {
    
        this.emit("event");
    
      }), opts.freq || 100 );
    }

    As a last resort, create an alias to this using self as an Identifier. This is extremely bug prone and should be avoided whenever possible.

    // 6.B.3
    
    function Device( opts ) {
      var self = this;
    
      this.value = null;
    
      stream.read( opts.path, function( data ) {
    
        self.value = data;
    
      });
    
      setInterval(function() {
    
        self.emit("event");
    
      }, opts.freq || 100 );
    }

    C. Use thisArg

    Several prototype methods of ES 5.1 built-ins come with a special thisArg signature, which should be used whenever possible

    // 6.C.1
    
    var obj;
    
    obj = { f: "foo", b: "bar", q: "qux" };
    
    Object.keys( obj ).forEach(function( key ) {
    
      // |this| now refers to `obj`
    
      console.log( this[ key ] );
    
    }, obj ); // <-- the last arg is `thisArg`
    
    // Prints...
    
    // "foo"
    // "bar"
    // "qux"

    thisArg can be used with Array.prototype.every, Array.prototype.forEach, Array.prototype.some, Array.prototype.map, Array.prototype.filter

  6. Misc

    This section will serve to illustrate ideas and concepts that should not be considered dogma, but instead exists to encourage questioning practices in an attempt to find better ways to do common JavaScript programming tasks.

    A. Using switch should be avoided, modern method tracing will blacklist functions with switch statements

    There seems to be drastic improvements to the execution of switch statements in latest releases of Firefox and Chrome. http://jsperf.com/switch-vs-object-literal-vs-module

    Notable improvements can be witnessed here as well: #13

    // 7.A.1.1
    // An example switch statement
    
    switch( foo ) {
      case "alpha":
        alpha();
        break;
      case "beta":
        beta();
        break;
      default:
        // something to default to
        break;
    }
    
    // 7.A.1.2
    // A alternate approach that supports composability and reusability is to
    // use an object to store "cases" and a function to delegate:
    
    var cases, delegator;
    
    // Example returns for illustration only.
    cases = {
      alpha: function() {
        // statements
        // a return
        return [ "Alpha", arguments.length ];
      },
      beta: function() {
        // statements
        // a return
        return [ "Beta", arguments.length ];
      },
      _default: function() {
        // statements
        // a return
        return [ "Default", arguments.length ];
      }
    };
    
    delegator = function() {
      var args, key, delegate;
    
      // Transform arguments list into an array
      args = [].slice.call( arguments );
    
      // shift the case key from the arguments
      key = args.shift();
    
      // Assign the default case handler
      delegate = cases._default;
    
      // Derive the method to delegate operation to
      if ( cases.hasOwnProperty( key ) ) {
        delegate = cases[ key ];
      }
    
      // The scope arg could be set to something specific,
      // in this case, |null| will suffice
      return delegate.apply( null, args );
    };
    
    // 7.A.1.3
    // Put the API in 7.A.1.2 to work:
    
    delegator( "alpha", 1, 2, 3, 4, 5 );
    // [ "Alpha", 5 ]
    
    // Of course, the `case` key argument could easily be based
    // on some other arbitrary condition.
    
    var caseKey, someUserInput;
    
    // Possibly some kind of form input?
    someUserInput = 9;
    
    if ( someUserInput > 10 ) {
      caseKey = "alpha";
    } else {
      caseKey = "beta";
    }
    
    // or...
    
    caseKey = someUserInput > 10 ? "alpha" : "beta";
    
    // And then...
    
    delegator( caseKey, someUserInput );
    // [ "Beta", 1 ]
    
    // And of course...
    
    delegator();
    // [ "Default", 0 ]
    

    B. Early returns promote code readability with negligible performance difference

    // 7.B.1.1
    // Bad:
    function returnLate( foo ) {
      var ret;
    
      if ( foo ) {
        ret = "foo";
      } else {
        ret = "quux";
      }
      return ret;
    }
    
    // Good:
    
    function returnEarly( foo ) {
    
      if ( foo ) {
        return "foo";
      }
      return "quux";
    }
  7. Native & Host Objects

    The basic principle here is:

    Don't do stupid shit and everything will be ok.

    To reinforce this concept, please watch the following presentation:

    “Everything is Permitted: Extending Built-ins” by Andrew Dupont (JSConf2011, Portland, Oregon)

    https://www.youtube.com/watch?v=xL3xCO7CLNM

  8. Comments

    Single line above the code that is subject

    Multiline is good

    End of line comments are prohibited!

    JSDoc style is good, but requires a significant time investment

  9. One Language Code

    Programs should be written in one language, whatever that language may be, as dictated by the maintainer or maintainers.

Appendix

Comma First.

Any project that cites this document as its base style guide will not accept comma first code formatting, unless explicitly specified otherwise by that project's author.


Creative Commons License
Principles of Writing Consistent, Idiomatic JavaScript by Rick Waldron and Contributors is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at github.com/rwldrn/idiomatic.js.

idiomatic.js's People

Contributors

akshatjoshii avatar boris-nekezov avatar dcherman avatar dsheiko avatar garu avatar greybax avatar isra00 avatar jamsyoung avatar jfroffice avatar jnosis avatar karlguillotte avatar kpsolo avatar leobalter avatar leobetosouza avatar lockys avatar marocchino avatar mathiasbynens avatar okuryu avatar pxrr avatar rkjun avatar rmurphey avatar rubystream avatar rwaldron avatar sindresorhus avatar sofish avatar stmoreau avatar svnlto avatar twokul avatar ulisesgascon avatar yambe2002 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

idiomatic.js's Issues

Namespace style suggestions?

Any suggestions to style guides for JavaScript Namespaces?

There is the Three.js ALLCAPS style of:

THREE.Vector3

Backbone's UpperCamelCase style of:

Backbone.View.extend(...)

And then Google's JavaScript style guide suggests that it is a lowCamelCase word.

Some generally recommendations would be nice.

var statements

This is actually not a real issue or bug report, but 2 personal opinions.

  1. var statements should always be at start of the respective scope (functions). The same goes for const and let from ECMAScript 6.

  2. var statements separated by commas is good, ok, but separated var declarations, one declaration by line should be good as well. It improves maintenance and readability. Example:

var foo = "bar";
var num = 1;
var undef;

This is much easier if you want to remove a single declaration at any time, including the first or the last one.

Anton Kovalyov wrote about this somewhere I don't remember right now.

TOC

Small thing but will be useful, especially as this grows. There needs to be a TOC linking to major sections using anchor links.

Potential issues with the `switchObj` approach if `hasOwnProperty` isn’t used

// 7.A.1.2
// A better approach would be to use an object literal or even a module:

var switchObj = {
    alpha: function() {
        // statements
        // a return
    }, 
    beta: function() {
        // statements
        // a return
    }, 
    default: function() {
        // statements
        // a return
    }
};

var switchModule = (function () {
    return {
        alpha: function() {
            // statements
            // a return
        }, 
        beta: function() {
            // statements
            // a return
        }, 
        default: function() {
            // statements
            // a return
        }
    };
})();

// 7.A.1.3
// If `foo` is a property of `switchObj` or `switchModule`, execute as a method...

( switchObj[ foo ] || switchObj.default )( args );

( switchModule[ foo ] || switchObj.default )( args );

// If you know and trust the value of `foo`, you could even omit the OR check
// leaving only the execution:

switchObj[ foo ]( args );

switchModule[ foo ]( args );

Note that there are potential issues with the switchObj approach if you can’t fully trust the value of foo, e.g. if foo === "toString".

var x = {};
x['toString']; // truthy
x['__proto__']; // truthy

So this approach should probably only be used if you can fully trust the value of foo to be either "alpha" or "beta" (or "default").
If not, you’ll need to use something like hasOwnProperty (but then again, that doesn’t work in Safari 2):

( ( switchObj.hasOwnProperty( foo ) && switchObj[ foo ] ) || switchObj.default )( args );
// or…
( switchObj.hasOwnProperty( foo ) && switchObj[ foo ] || switchObj.default )( args );

Numbers parsing

parseInt("1234567890987654321", 10); // => 1234567890987654400
+"1234567890987654321"; // => 1234567890987654400
~~"1234567890987654321" // => -1318314752 
"1234567890987654321" >> 0 // => -1318314752
"1234567890987654321" >>> 0 // => 2976652544

All of them are incorrect, but I would rather expect first two rather than other results.

for ( prop in object ) {

not sure I would consider this best practice. jQuery is pretty much the only lib doing this, and IMHO it's not easy to read because you're taking what was a single "segment" (prop in object) and breaking it apart with whitespace into several more

Single or double quotes

Is it worth putting in a note about choosing to use single or double quotes? I prefer single (saves my pinky muscles) but obviously you guys prefer double. Consistency is important either way.

Question Regarding 5.2.1

What is the purpose of wrapping the object definition inside a closure? You can still blow away the constructor on the global object the same way you could if the definition was not inside a closure.

I'm just looking for some enlightenment.

Naming for files?

Hi, I couldn't find a naming convention for files here, it will be useful for me. Using camelcase, hyphen or underscore or dot to separate? (personally I prefer underscore)

Guide recommends 2 space indentation but examples use 4 spaces.

(and there's a runaway &mdash; throwing a party.)

The guide:

  • For readability, I always recommend setting your editor's indent size to two characters — this means two spaces or two spaces representing a real tab.

An example example:

// 2.B.1.1
// Variables
var foo = "bar",
    num = 1,
    undef;

4 space (or a tab width of 4) indentation makes more sense as it lines up nested var declarations nicely, IMO.

`switchObj` performance

I absolutely love the elegance and reusability of the switchObj pattern (even if it’s slower), but I’d like to get this straight.

See http://twatlr.com/thread/140407213511544833
Here’s @felixge’s test case: http://jsperf.com/object-vs-regular-switch

For my Velocity preso I created this test: http://jsperf.com/switch-vs-object-literal-vs-module/2 But it seemed regular switch was still faster. Any idea what’s up with that?

Rick’s test case only seems to test for the case where foo is a defined key in the switchObj, and doesn’t have the hasOwnProperty check. Perhaps that’s what caused the confusion?

/cc @rwldrn @jdalton @felixge

If operators are supposed to be surrounded by single space, there is a misprint in the samples

I'm writing a code sniffer for Idiomatic.js (https://github.com/dsheiko/jscodesniffer)
When testing code samples from https://github.com/rwldrn/idiomatic.js/ ran into following:

// 2.B.2.3
return number * factorial( number-1 );

Operators usually are surrounded by one space, which sums up nicely with "Use whitespace to promote readability". But here there is no spaces around "-" operator. Is it an inconsistency or Idiomatic just has no convention for operators?

No standard on line length.

You don't mention anything about maximum line length. Which either means there isn't one or you forgotten about it.

My fork in 6.E mentions maximum line length of 100 and some style definitions on how to break lines up.

Even if you don't set a maximum line limit there is no standard way to have multi line expressions in idiomatic.js

How to write chaining ?

myObject.aMethod(1)
  .withCallback(function(err, foo) {
    // some code
  })
  .withCallbackEpisodeTwo(function(err, bar) { 
    // more code
  });

?
The code written above is not suggestion, just an example amongst other that needs consistency and examples in the guide IMHO.

var statements and multiline

This isn't a bug, but this repo was recently discussed on reddit.

http://www.reddit.com/r/javascript/comments/15m252/best_javascript_coding_style_guide_i_have_found/

Most people seem to agree that comma separating var statements across multiline is bad form. (Or at least, I do)

It's a good starting point, although, I disagree about var assignments, I prefer multiline var.

var thing,
   that

vs

 var thing
 var that

The reason why I disagree is because when yanking variables around, it's much easier to move one complete expression verse half an expression or part of one. This doesn't include/exclude good practises of where to place your vars.


Any thoughts?

Cheers

Whitespace on empty lines

Section 3.B.2.3 has a few empty lines that contain 4 spaces.

There is also one instance of js rather than javascript

!!~array.indexOf (please reconsider)

Section "3.B.2.3" calls out

!!~array.indexOf("a");

as being overly clever. I feel like this is concise and idiomatic JavaScript and should not necessarily be looked down upon.

I am asking that this be reconsidered.

try/catch format?

The guide doesn't include an example for try/catch. Should it use the same format as if/else?:

try {
  // statements
} catch( e if e instanceof TypeError ) {
  // statements
}

Also, is it recommended to use the single letter e or a more descriptive identifier like err or error?

Line up variables

Just a thought, is it better to soft indent variable names or is it acceptable to list them all with the same indentation.

// soft indent
var three,
  example,
  vars;

// line up with first var
var three,
    example,
    vars;

void 0

a fast, short and reliable method to check undefined:

if(a === void 0){

}
  • compare with undefined is not reliable, undefined is a var over-writable.
  • compare with local undefined variable need to declare variable.

consider adding comment upon first use of non-browser idioms in samples, like .emit()

By 'non-native' I mean 'not in the box for most modern browsers'. Reading top-to-bottom the sample switch at one point from browser-based to Node.js-based (seem to be 'B. Faces of this', which was the topic I most interested in).

stream.read(...); // using an non-native function, further reading: Node.js 
this.emit('start'); // using an non-native function, further reading: socket.io or Node.js

Personal note: I only dabble in javascript, so this is a newbie request. Please be kind if I'm out of my depth and this is way off.

Question regarding 6.B.3

"As a last resort, create an alias to this using self as an Identifier. This is extremely bug prone and should be avoided whenever possible."

Would you mind explaining, or pointing me in the direction of some resources on why this is extremely bug prone please?

I'm currently exploring existing coding standards and sometimes they lack reasons/explanation as to why.

I did some googling and could only find information on the this but not why aliasing it can be bug prone.

Thanks.

Contradictory whitespace policy

"For readability, I always recommend setting your editor's indent size to two characters — this means two spaces or two spaces representing a real tab."

The rest of the document uses 4 spaces. This tripped me up for about a half hour. Suggest compromising at 3 spaces.

`default` is a reserved word in JS — use something else for the `switchObj` approach

default is a reserved word in JS. At least Opera and Safari choke on switchObj.default:

Syntax error at line 9 while loading:
default)(a, b, inver
^
expected identifier, got keyword 'default'
Syntax error at line 8 while loading:
[type] || switchObj.default)(a, b, inver
--------------------^
expected identifier, got keyword 'default'

Rick, any suggestions for the new name? _default? defaultBehavior? standard?

Opinionated attack on comma-first

There is an off hand comment about comma first being bad at the bottom.

It likes to a es-discuss thread with this code

var middleware = [ context,
                 , csrf
                 , xsite
                 , head
                 , considtional
                 , error
                 ];

which is just an example of bad code. It clearly should be

var middleware = [ context
                 ,
                 , csrf
                 , xsite
                 , head
                 , considtional
                 , error
                 ];

Yes you can write code where it's not easy to spot a mistake with comma-first but you can do the same with comma last.

var middleware = [ context,
                   csrf,
                   xsite
                   head,
                   considtional,
                   error ];

I personally think this comment is just noise and should be removed.

not idiomatic

This is just a comment not an issue per se

Like the effort, however what is produced is NOT idiomatic but rather
conformative

Keep up up the good work

Nikos

Test Examples

Providing some vague/quick examples of "testing facilities" in the index README.md would go a long way here.

Updates to contributors and translations list

Hey @rwldrn , can you take a look at last two commits here and tell me if that looks ok to you.

https://github.com/Gavrisimo/idiomatic.js/commits/wip.serbian

I didn't want to send PR for that, I thought it will be better to ask first, and then if it's ok with you to send a PR.

Basically what I have done is that I updated contributors list in all translations and I have made translations list same in all translations. The only difference now between translation and original English version is that translated version has ORIGINAL link at top of translations list. Oh and also they are all written in English, since updating translations list in Korean or Chinese(or any language other then English and native) is a bit hard... :)

Dutch translation

Since someone else beat me to it regarding Nicolas' Idiomatic CSS document (but will help when needed) and noone has taken care of a Dutch translation of the idiomatic JS document yet I will take ownership of this one if that's OK. :)

JSLint/JSHint Settings?

Would love to see this documentation made available as config settings for JSLint/JSHint so these guidelines can actually be practically enforced.

The 'this' keyword

Hi,

It would be great to add a note on the this to be used in another context. I have seen:

var that = this;
var self = this;
var cc = this;
var _this = this;
var $this = this;

Choosing one and not mixing them helps code readability.

Thanks
Karl

Comma first ?

hello,

All of that are very very instructive, but at the end i don't understand something, why begining with a comma is not a good practice ?
Can someone give me some explications ?
I always done that and i find it more lisible, it helps in not forget it !

var myObject = {
  one : "one"
  , two : "two"
  , three : "three"
    four : "four"
  , five : "five"
};

we can see where is the error instantly !

Thanks.

fr_FR tabulations

The fr_FR translation doesn't meet the recomendations : tabulations are composed of 4 spaces and not 2.

parseInt vs ~~

The doc currently says

parseInt( num, 10 );
// is the same as...
~~num;

That's only true for Numbers or digits-only strings. parseInt is commonly used to get numbers out of value strings, like parseInt('10px', 10), whereas ~~'10px' gives you a 0. I think this difference in behaviour (though obvious if you know what ~ is) should be noted somehow somehow to avoid confusion.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.