Normas de JavaScript

Índice

  1. Tipos
  2. Objetos
  3. Arrays
  4. Strings
  5. Funciones
  6. Propiedades
  7. Variables
  8. Expresiones condicionales e igualdad
  9. bloque
  10. Comentarios
  11. Espacios en blanco
  12. Comas
  13. Puntos y comas
  14. Conversión de tipos
  15. Convenciones de nomenclatura
  16. Accesores
  17. Constructores
  18. Eventos
  19. Módulos
  20. jQuery
  21. Compatibilidad con ES5
  22. Separación de HTML, CSS y JavaScript
  23. Usar jsHint
  24. Herramientas de front-end

Tipos

  • Valores primitivos: equivalente a pasar por valor (los objetos de JavaScript ofrecen literales), usa literales para crear objetos.
    • string
    • number
    • boolean
    • null
    • undefined
var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9

Tipos complejos: equivalente a pasar por referencia.

  • object
  • array
  • function
var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Objetos

  • Usa literales para crear objetos.
    // bad
    var item = new Object();
    
    // good
    var item = {};
    
  • No uses palabras reservadasreserved wordscomo claves.
    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };
    

Arrays

  • Usa literales para crear arrays.
    // bad
    var items = new Array();
    
    // good
    var items = [];
    
  • Si no sabes la longitud del array, usa push.
    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
    
  • Cuando necesites copiar un array, usa slice. jsPerf.
    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
    
  • Usa slice para convertir objetos similares a arrays en arrays.
    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }
    

Strings

  • Usa comillas simples para strings.''(porque la mayoría de las veces nuestras strings. Especialmente HTML aparecerá")。
    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
    
  • Las strings de más de 80 caracteres (algunos especifican 140, el proyecto puede definirlo) deben usar concatenación de strings para realizar saltos de línea.
  • Nota: si se usa en exceso, la concatenación de strings largos puede afectar el rendimiento.
    // bad
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // bad
    var errorMessage = 'This is a super long error that \
    was thrown because of Batman. \
    When you stop to think about \
    how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // good
    var errorMessage = 'This is a super long error that ' +
      'was thrown because of Batman.' +
      'When you stop to think about ' +
      'how Batman had anything to do ' +
      'with this, you would get nowhere ' +
      'fast.';
    
  • Al programar, usa join en lugar de concatenación de strings para construir strings.
    var items,
        messages,
        length, i;
    
    messages = [{
        state: 'success',
        message: 'This one worked.'
    },{
        state: 'success',
        message: 'This one worked as well.'
    },{
        state: 'error',
        message: 'This one did not work.'
    }];
    
    length = messages.length;
    
    // bad
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // good
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = messages[i].message;
      }
    
      return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
    }
    

Funciones

  • Expresiones de función:
    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 有名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用函数表达式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
    
  • Nunca declares una función dentro de un bloque que no sea una función; asigna esa función a una variable. Los navegadores te lo permiten, pero lo interpretan de manera diferente.
  • Nota:ECMA-262 definebloquecomo un conjunto de declaraciones, la declaración de una función no es una declaración.Lee la explicación de ECMA-262 sobre este problema.。
    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
    
  • Nunca nombres un parámetroarguments, esto anulará elargumentsobjeto pasado dentro del ámbito de la función.
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }
    

Propiedades

  • Cuando uses variables y nombres de variables especiales no válidos, puedes usar corchetes para acceder a las propiedades (.preferido).
    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
    

Variables

  • Siempre usavarpara declarar variables; si no lo haces, se generarán variables globales. Debemos evitar contaminar el espacio de nombres global.
    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
    
  • Usa unvary nuevas líneas para declarar múltiples variables, con sangría de 4 espacios.
    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
  • Declara al final las variables sin asignar; esto es útil cuando quieres referenciar variables previamente asignadas.
    // bad
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // bad
    var i, items = getItems(),
        dragonball,
        goSportsTeam = true,
        len;
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball,
        length,
        i;
    
  • Declara las variables en la parte superior del ámbito para evitar problemas relacionados con la declaración y asignación de variables.
    // bad
    function() {
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // good
    function() {
      var name = getName();
    
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // bad
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }
    
    // good
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    

Expresiones condicionales e igualdad

  • Usa adecuadamente===y!==y==y!=。
  • Usa adecuadamente los operadores lógicos en expresiones.
  • La coerción de tipos en expresiones condicionales sigue las siguientes reglas:
    • Objetosse evalúa comotrue
    • Undefinedse evalúa comofalse
    • Nullse evalúa comofalse
    • Booleanosse evalúa comoel valor del booleano
    • NúmerosSi es+0, -0, or NaNse evalúa comofalse, de lo contrario estrue
    • StringsSi es una cadena vacía''se evalúa comofalse, de lo contrario estrue
      if ([0]) {
        // true
        // An array is an object, objects evaluate to true
      }
      
  • Usa atajos.
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
    
  • LeeTruth Equality and JavaScriptpara obtener más información.

bloque

  • Usa llaves para todos los bloques de varias líneas.
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
    
  • Usa/** ... */para comentarios de varias líneas, incluyendo descripción, tipos especificados, valores de parámetros y valores de retorno.
    // 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;
    }
    
  • Usa//para comentarios de una sola línea. Realiza el comentario en la línea superior al objeto comentado, y deja una línea en blanco antes del comentario.
    // bad
    var active = true;  // is current tab
    
    // good
    // is current tab
    var active = true;
    
    // bad
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
    // good
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
  • Si tienes un problema que necesitas revisar nuevamente, o si sugieres una solución que debe implementarse, añade al inicio de tu comentarioFIXMEoTODOpara ayudar a otros a entender rápidamente.
    function Calculator() {
    
      // FIXME: shouldn't use a global here
      total = 0;
    
      return this;
    }
    
    function Calculator() {
    
      // TODO: total should be configurable by an options param
      this.total = 0;
    
      return this;
    }
    
  • Documentación conforme a las normas. Cuando se necesite documentación, puedes probarjsdoc。

Espacios en blanco

  • La indentación y el formateo ayudan al equipo a localizar y corregir errores en el código más rápidamente.
  • Configura el tabulador como 4 espacios.
    // bad
    function() {
    ∙∙var name;
    }
    
    // bad
    function() {
    ∙var name;
    }
    
    // good
    function() {
    ∙∙∙∙var name;
    }
    
  • Pon un espacio antes de las llaves.
    // bad
    function test(){
      console.log('test');
    }
    
    // good
    function test() {
      console.log('test');
    }
    
    // bad
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
    // good
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
  • Usa sangría al hacer cadenas de métodos largas.
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // good
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
        .attr('width',  (radius + margin) * 2).append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    
    // good
    var leds = stage.selectAll('.led')
        .data(data)
      .enter().append('svg:svg')
        .class('led', true)
        .attr('width',  (radius + margin) * 2)
      .append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    

Comas

  • No pongas las comas al principio.
    // bad
    var once
      , upon
      , aTime;
    
    // good
    var once,
        upon,
        aTime;
    
    // bad
    var hero = {
        firstName: 'Bob'
      , lastName: 'Parr'
      , heroName: 'Mr. Incredible'
      , superPower: 'strength'
    };
    
    // good
    var hero = {
      firstName: 'Bob',
      lastName: 'Parr',
      heroName: 'Mr. Incredible',
      superPower: 'strength'
    };
    
  • No añadas comas sobrantes; esto puede causar errores en IE. Además, una coma adicional hará que algunas implementaciones de ES3 calculen incorrectamente la longitud de los arrays.
    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];
    

Puntos y comas

  • Siempre añade un punto y coma al final de las sentencias.
    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
    

Conversión de tipos

  • Realiza la conversión de tipos al inicio de la sentencia.
  • Strings:
    //  => this.reviewScore = 9;
    
    // bad
    var totalScore = this.reviewScore + '';
    
    // good
    var totalScore = '' + this.reviewScore;
    
    // bad
    var totalScore = '' + this.reviewScore + ' total score';
    
    // good
    var totalScore = this.reviewScore + ' total score';
    
  • Para números, usaparseInty siempre incluye la base de conversión, comoparseInt(value, 10)。
  • var inputValue = '4';
    
    // bad
    var val = new Number(inputValue);
    
    // bad
    var val = +inputValue;
    
    // bad
    var val = inputValue >> 0;
    
    // bad
    var val = parseInt(inputValue);
    
    // good
    var val = Number(inputValue);
    
    // good
    var val = parseInt(inputValue, 10);
    
    // good
    /**
     * parseInt was the reason my code was slow.
     * Bitshifting the String to coerce it to a
     * Number made it a lot faster.
     */
    var val = inputValue >> 0;
    
  • Booleanos:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
    

Convenciones de nomenclatura

  • Evita nombres de un solo carácter; haz que tus nombres de variables sean descriptivos.
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
    
  • Usa la convención camelCase al nombrar objetos, funciones e instancias.
    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    var this-is-my-object = {};
    function c() {};
    var u = new user({
      name: 'Bob Parr'
    });
    
    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {};
    var user = new User({
      name: 'Bob Parr'
    });
    
  • Usa PascalCase (mayúscula inicial) al nombrar constructores o clases.
    // bad
    function user(options) {
      this.name = options.name;
    }
    
    var bad = new user({
      name: 'nope'
    });
    
    // good
    function User(options) {
      this.name = options.name;
    }
    
    var good = new User({
      name: 'yup'
    });
    
  • Antepón un guion bajo al nombrar propiedades privadas_:
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
    
  • Cuando guardes una referencia athisusaself(python 风格), evitathis issue.Angular recomienda usarvm(MVVM模式中view-model):
    // good
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    

Accesores

  • Las funciones de acceso a propiedades no son necesarias.
  • Si realmente tienes funciones de acceso, usa getVal() y setVal('hello'),java getter、setter风格ojQuery风格。
  • Si la propiedad es booleana, usa isVal() o hasVal().
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    
  • Puedes crear funciones get() y set(), pero mantenlas consistentes.
    function Jedi(options) {
      options || (options = {});
      var lightsaber = options.lightsaber || 'blue';
      this.set('lightsaber', lightsaber);
    }
    
    Jedi.prototype.set = function(key, val) {
      this[key] = val;
    };
    
    Jedi.prototype.get = function(key) {
      return this[key];
    };
    

Constructores

  • Asigna métodos al prototipo del objeto, en lugar de sobrescribir el prototipo con un nuevo objeto; sobrescribir el prototipo causará problemas con la herencia.
    function Jedi() {
      console.log('new jedi');
    }
    
    // bad
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // good
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
    
  • Los métodos pueden devolverthispara que los métodos sean encadenables.
    // bad
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20) // => undefined
    
    // good
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
      return this;
    };
    
    var luke = new Jedi();
    
    luke.jump()
      .setHeight(20);
    
  • Puedes escribir un método toString() personalizado, pero asegúrate de que funcione correctamente y no tenga efectos secundarios.
    function Jedi(options) {
      options || (options = {});
      this.name = options.name || 'no name';
    }
    
    Jedi.prototype.getName = function getName() {
      return this.name;
    };
    
    Jedi.prototype.toString = function toString() {
      return 'Jedi - ' + this.getName();
    };
    

Eventos

  • Al adjuntar datos a eventos, pasa un hash en lugar de un valor primitivo; esto permite que futuros colaboradores añadan más datos a los datos del evento sin tener que buscar y actualizar el manejador de ese evento.
    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });
    

    Mejor:

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

Módulos

  • Este archivo debe nombrarse en camelCase, estar en una carpeta con el mismo nombre, y coincidir el nombre al exportarlo.
  • Para bibliotecas de API públicas, considera añadir un método llamado noConflict() que establezca el módulo exportado a la versión anterior y lo devuelva.
  • Siempre declara en la parte superior del módulo'use strict';, e importa[JSHint规范](http://jshint.com/):
    // fancyInput/fancyInput.js
    
    (function(global) {
      'use strict';
    
      var previousFancyInput = global.FancyInput;
    
      function FancyInput(options) {
        this.options = options || {};
      }
    
      FancyInput.noConflict = function noConflict() {
        global.FancyInput = previousFancyInput;
        return FancyInput;
      };
    
      global.FancyInput = FancyInput;
    })(this);
    

jQuery

  • Para objetos jQuery, comienza con$al principio, para distinguirlos de los nodos DOM nativos.
    // bad
    var menu = $(".menu");
    
    // good
    var $menu = $(".menu");
    
  • Cachea las consultas de jQuery
    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...stuff...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      var $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...stuff...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
    
  • Para consultas DOM, usa selectores en cascada$('.sidebar ul')o$('.sidebar ul'),jsPerf.
  • Para consultas de objetos jQuery con ámbito, usafind:
    // bad
    $('.sidebar', 'ul').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good (slower)
    $sidebar.find('ul');
    
    // good (faster)
    $($sidebar[0]).find('ul');
    
  • Usa el evento ready de document solo una vez por página, para facilitar la depuración y el seguimiento del flujo de comportamiento.
    $(function(){
       //do your page init.  
    });
    
  • Utiliza eventosjQuery.onseparándolos de la página hacia archivos JavaScript.
    // bad
    <a id="myLink" href="../index.html" onclick="myEventHandler();"></a>
    
    // good
    <a id="myLink" href="../index.html"></a>
    
    $("#myLink").on("click", myEventHandler);
    
  • Para Ajax, usa el patrón de promesas.
    // bad
        $.ajax({
            ...
            success : function(){
            },
            error : function(){
            } 
        })
    
        // good
        $.ajax({.
            ..
        }).then( function( ){
            // success
        }, function( ){
            // error
        })
    
  • Aprovecha el objeto deferred de promise para resolver problemas de registro diferido.
    var dtd = $.Deferred(); // 新建一个deferred对象
      var wait = function(dtd){
        var tasks = function(){
          alert("执行完毕!");
          dtd.resolve(); // 改变deferred对象的执行状态
        };
        setTimeout(tasks,5000);
        return dtd;
      };
    
  • El Style en HTML y el style en JavaScript deben trasladarse a clases en CSS; en HTML y JavaScript se introduce class en lugar de usar style directamente.

Compatibilidad con ECMAScript 5

En la medida de lo posible, adoptar métodos ES5, especialmente los métodos map, filter y forEach de los arrays para simplificar el desarrollo diario. En navegadores IE antiguos, introducirES5-shim. O también se puede considerar introducirunderscore、lodashbibliotecas auxiliares comunes.
- ReferenciaKangaxde ES5compatibility table。

Separación de HTML, CSS y JavaScript

  • La estructura DOM de la página usa HTML, los estilos usan CSS, y las operaciones dinámicas del DOM se hacen con JavaScript. No mezclarlos en HTML.
  • Separarlos en archivos de diferentes tipos, y enlazar los archivos mediante link.
  • Los nombres de variables en HTML, CSS y JavaScript deben tener valor de negocio. En CSS se nombran en minúsculas separadas por guiones medios, y en JavaScript se usa notación camelCase con la primera letra en minúscula.
  • Se pueden introducir en CSSBootstrap, marcos de diseño responsive conocidos como Foundation, así como herramientas SASS y LESS para escribir CSS.
  • Para CSS y JavaScript se recomienda combinarlos en un solo archivo para reducir el número de conexiones Ajax. También se puede introducir el modo de carga AMD (Require.js).
  • Para la mayoría de los sistemas internos de gestión empresarial, se puede intentar adoptar el front-endframework MVCpara organizar el código. Como Angular, React con arquitectura Flux, Knockout, etc.
  • Para la compatibilidad, se puede usarModernizrbibliotecas de normalización como soporte.

Usar jsHint

  • En proyectos de front-end se recomienda introducirjshintun complemento para estandarizar las normas de codificación del proyecto. Además de una configuración completa del IDE.
  • Nota: jshint requiere introducir la herramienta Node.js y los complementos grunt o gulp; se recomienda un servidor privado npm de Node.js a nivel empresarial.

Herramientas de front-end

  • La herramienta de gestión de paquetes JavaScript de terceros para front-end bower(bower install jQuery), bower puede implementar la resolución de dependencias, descarga, gestión de actualizaciones, etc. de bibliotecas de terceros. Se recomienda establecer un servidor privado bower a nivel empresarial.
  • Herramientas de construcción de front-end: se pueden adoptar las herramientas grunt o gulp; pueden realizar compresión de html, css, js, validación, pruebas, fusión de archivos, watch y liveload, y todas las demás tareas front-end. Se recomienda un servidor privado npm de Node.js a nivel empresarial.
  • IDE de desarrollo front-end: WebStorm (Idea) y Sublime son los mejores. El equipo del proyecto debe unificar el IDE. La configuración unificada del IDE es muy importante.

Artículos relacionados

Fuente: http://greengerong.github.io/blog/2015/05/09/qian-duan-javascriptgui-fan