Руководство по стилю JavaScript

Содержание

  1. Типы
  2. Объекты
  3. Массивы
  4. Строки
  5. Функции
  6. Свойства
  7. Переменные
  8. Условные выражения и равенство
  9. block
  10. Комментарии
  11. Пробельные символы
  12. Запятые
  13. Точки с запятой
  14. Приведение типов и неявное преобразование
  15. Соглашения об именовании
  16. Методы доступа
  17. Конструкторы
  18. Events
  19. Модули
  20. jQuery
  21. Совместимость с ES5
  22. Разделение HTML, CSS и JavaScript
  23. Использование jsHint
  24. Инструменты фронтенда

Типы

  • Примитивные значения: эквивалентно передаче по значению (все объекты JavaScript предоставляют литералы), используйте литералы для создания объектов.
    • string
    • number
    • boolean
    • null
    • undefined
var foo = 1,
    bar = foo;

bar = 9;

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

Сложные типы: эквивалентно передаче по ссылке

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

bar[0] = 9;

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

Объекты

  • Используйте литеральные значения для создания объектов.
    // bad
    var item = new Object();
    
    // good
    var item = {};
    
  • Не используйте зарезервированные словазарезервированные словав качестве ключей.
    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };
    

Массивы

  • Используйте литеральные значения для создания массивов.
    // bad
    var items = new Array();
    
    // good
    var items = [];
    
  • Если вы не знаете длину массива, используйте push.
    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
    
  • Используйте slice, когда вам нужно скопировать массив. jsPerf.
    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
    
  • Используйте slice для преобразования объектов, подобных массивам, в массивы.
    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }
    

Строки

  • Используйте одинарные кавычки для строк''(Потому что в большинстве случаев наши строки, особенно HTML, будут содержать")。
    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
    
  • Строки, превышающие 80 символов (некоторые указывают 140, проект может определить своё собственное значение), должны использовать конкатенацию строк для переноса строк.
  • Примечание: При чрезмерном использовании длинная конкатенация строк может повлиять на производительность.
    // 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.';
    
  • При программировании используйте join вместо конкатенации строк для построения строк.
    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>';
    }
    

Функции

  • Функциональные выражения:
    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 有名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用函数表达式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
    
  • Никогда не объявляйте функцию в блоке, не являющемся функциональным; вместо этого присвойте эту функцию переменной. Браузеры позволяют это делать, но они обрабатывают это по-разному.
  • Примечание:ECMA-262 определяетblockкак набор инструкций; объявление функции не является инструкцией.Прочитайте пояснение ECMA-262 по этому вопросу.。
    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
    
  • Никогда не называйте параметрarguments, это переопределитargumentsобъект, переданный в область видимости функции.
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }
    

Свойства

  • При использовании переменных и специальных недопустимых имён переменных можно использовать квадратные скобки для доступа к свойствам (.предпочтительно).
    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
    

Переменные

  • Всегда используйтеvarдля объявления переменных. В противном случае будут созданы глобальные переменные, и нам следует избегать загрязнения глобального пространства имён.
    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
    
  • Используйте одинvarи переводы строк, чтобы объявить несколько переменных, с отступом в 4 пробела.
    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
  • Объявляйте переменные без значения последними; это полезно, когда вы хотите ссылаться на ранее присвоенные переменные.
    // 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;
    
  • Объявляйте переменные в начале области видимости, чтобы избежать проблем, связанных с объявлением и присваиванием переменных.
    // 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;
    }
    

Условные выражения и равенство

  • Используйте соответствующим образом===и!==и==и!=。
  • Используйте логические операции в выражениях соответствующим образом.
  • Приведение типов в условных выражениях следует этим правилам:
    • Объектывычисляются какtrue
    • Undefinedвычисляется какfalse
    • Nullвычисляется какfalse
    • Логические значениявычисляются каклогическое значение
    • ЧислаЕсли это+0, -0 или NaNвычисляется какfalse, иначе какистина
    • СтрокиЕсли это пустая строка''она вычисляется какfalse, иначе какtrue
      if ([0]) {
        // true
        // An array is an object, objects evaluate to true
      }
      
  • Используйте сокращения.
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
    
  • ПрочитайтеTruth Equality and JavaScript, чтобы узнать больше.

block

  • Используйте фигурные скобки для всех многострочных блоков.
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
    
  • Используйте/** ... */для многострочных комментариев, включая описания, указанные типы, значения параметров и возвращаемые значения.
    // 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;
    }
    
  • Используйте//для однострочных комментариев. Размещайте однострочный комментарий над комментируемым объектом и оставляйте пустую строку перед комментарием.
    // 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;
    }
    
  • Если у вас есть проблема, которую нужно пересмотреть, или если вы предлагаете решение, которое нужно реализовать, начните комментарий сFIXMEилиTODOчтобы помочь другим быстро понять.
    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;
    }
    
  • Для документации, соответствующей спецификации, когда документация нужна, можно попробоватьjsdoc。

Пробельные символы

  • Отступы и форматирование помогают команде быстрее находить и исправлять ошибки в коде.
  • Настройте табуляцию на 4 пробела.
    // bad
    function() {
    ∙∙var name;
    }
    
    // bad
    function() {
    ∙var name;
    }
    
    // good
    function() {
    ∙∙∙∙var name;
    }
    
  • Ставьте пробел перед открывающей фигурной скобкой.
    // 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'
    });
    
  • Используйте отступы при длинных цепочках методов.
    // 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);
    

Запятые

  • Не ставьте запятые в начале.
    // 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'
    };
    
  • Не добавляйте лишние завершающие запятые. Это может вызвать ошибки в IE, а при наличии лишней запятой некоторые реализации ES3 посчитают дополнительную длину массива.
    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];
    

Точки с запятой

  • Всегда добавляйте точки с запятой в конце инструкций.
    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
    

Приведение типов и неявное преобразование

  • Выполняйте преобразование типов в начале инструкции.
  • Строки:
    //  => 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';
    
  • Для чисел используйтеparseIntи всегда указывайте основание системы счисления для преобразования типов, например,parseInt(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;
    
  • Логические значения:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
    

Соглашения об именовании

  • Избегайте односимвольных имён; делайте имена переменных описательными.
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
    
  • Используйте camelCase при именовании объектов, функций и экземпляров.
    // 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'
    });
    
  • Используйте PascalCase при именовании конструкторов или классов.
    // 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'
    });
    
  • Предваряйте приватные свойства символом подчёркивания_:
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
    
  • При сохранении ссылки наthisиспользоватьself(python 风格), избегайтеthis issue. Angular рекомендует использоватьvm(MVVM模式中view-model):
    // good
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    

Методы доступа

  • Функции доступа для свойств не требуются.
  • Если у вас есть функции доступа, используйте getVal() и setVal('hello'),java getter、setter风格илиjQuery风格。
  • Если свойство является булевым, используйте isVal() или hasVal().
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    
  • Вы можете создавать функции get() и set(), но поддерживайте их единообразие.
    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];
    };
    

Конструкторы

  • Назначайте методы прототипу объекта вместо перезаписи прототипа новым объектом. Перезапись прототипа может вызвать проблемы с наследованием.
    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');
    };
    
  • Методы могут возвращатьthisчтобы методы можно было объединять в цепочки.
    // 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);
    
  • Вы можете написать собственный метод toString(), но убедитесь, что он работает правильно и не имеет побочных эффектов.
    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();
    };
    

Events

  • При привязке данных к событиям передавайте хеш вместо примитивного значения. Это позволяет последующим участникам добавлять больше данных к данным события без необходимости искать и обновлять обработчик события для этого события.
    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });
    

    Лучше:

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

Модули

  • Файл должен быть назван в camelCase, размещён в папке с тем же именем и иметь согласованное имя при экспорте.
  • Для библиотек с публичным API рассмотрите возможность добавления метода с именем noConflict(), который устанавливает экспортируемый модуль на предыдущую версию и возвращает его.
  • Всегда объявляйте в начале модуля'use strict';, import[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

  • Для объектов jQuery начинайте с$в начале, чтобы отличать их от нативных узлов DOM.
    // bad
    var menu = $(".menu");
    
    // good
    var $menu = $(".menu");
    
  • Кэшируйте 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'
      });
    }
    
  • Используйте каскадирование для DOM-запросов$('.sidebar ul')или$('.sidebar ul'),jsPerf.
  • Для запросов к объектам jQuery с ограниченной областью используйтеfind:
    // 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');
    
  • Используйте событие document ready только один раз на страницу, что облегчает отладку и отслеживание потока поведения.
    $(function(){
       //do your page init.  
    });
    
  • Используйте событияjQuery.onвынесенные со страницы в файлы JavaScript.
    // bad
    <a id="myLink" href="../index.html" onclick="myEventHandler();"></a>
    
    // good
    <a id="myLink" href="../index.html"></a>
    
    $("#myLink").on("click", myEventHandler);
    
  • Используйте подход на основе promise для Ajax.
    // bad
        $.ajax({
            ...
            success : function(){
            },
            error : function(){
            } 
        })
    
        // good
        $.ajax({.
            ..
        }).then( function( ){
            // success
        }, function( ){
            // error
        })
    
  • Используйте отложенные объекты promise для решения проблем с отложенной регистрацией.
    var dtd = $.Deferred(); // 新建一个deferred对象
      var wait = function(dtd){
        var tasks = function(){
          alert("执行完毕!");
          dtd.resolve(); // 改变deferred对象的执行状态
        };
        setTimeout(tasks,5000);
        return dtd;
      };
    
  • Перенесите стили из HTML и JavaScript в CSS-классы; используйте классы в HTML и JavaScript вместо прямых стилей.

Совместимость с ECMAScript 5

По возможности используйте методы ES5, особенно методы map, filter и forEach для массивов, чтобы упростить повседневную разработку. В старых браузерах IE подключитеES5-shim. Или также можно рассмотреть подключениеunderscore、lodashобщие вспомогательные библиотеки.
- СсылкаKangaxES5таблица совместимости。

Разделение HTML, CSS и JavaScript

  • Используйте HTML для структуры DOM страницы, CSS для стилей, а JavaScript для динамических операций с DOM. Не смешивайте их в HTML.
  • Разделите на разные типы файлов и подключайте файлы.
  • Имена переменных в HTML, CSS и JavaScript должны нести бизнес-смысл. В CSS используются имена из строчных букв, разделённые дефисами, а в JavaScript — camelCase с первой строчной буквой.
  • В CSS можно подключитьBootstrap, Foundation и другие известные фреймворки адаптивного дизайна. Также используйте инструменты SASS и LESS для написания CSS.
  • Для CSS и JavaScript рекомендуется объединить их в один файл, чтобы уменьшить количество Ajax-соединений. Также можно подключить метод загрузки AMD (Require.js).
  • Для большинства внутренних корпоративных систем управления можно попробовать применить фронтендMVC-фреймворкидля организации кода. Например, Angular, React + flux архитектура, Knockout и т. д.
  • Для совместимости можно использоватьModernizrбиблиотеки спецификаций для помощи.

Использование jsHint

  • Рекомендуется внедрятьjshintплагины в фронтенд-проектах, чтобы стандартизировать правила кодирования проекта. Также иметь полный набор конфигураций IDE.
  • Примечание: jshint требует внедрения инструментов nodejs grunt или gulp плагинов. Рекомендуется корпоративный приватный сервер nodejs npm.

Инструменты фронтенда

  • Инструмент управления сторонними пакетами JavaScript для фронтенда bower (bower install jQuery), bower может реализовать разрешение зависимостей, загрузку, управление обновлениями и т. д. для сторонних библиотек. Рекомендуется настроить корпоративный приватный сервер bower.
  • Для инструментов сборки фронтенда можно использовать инструменты grunt или gulp для выполнения всех фронтенд-задач, таких как сжатие html, css, js, валидация, тестирование, объединение файлов, watch и liveload. Рекомендуется корпоративный приватный сервер nodejs npm.
  • IDE для фронтенд-разработки: WebStorm (Idea) и Sublime — лучшие. Команда проекта должна использовать единую IDE. Единая конфигурация IDE очень важна.

Связанные статьи

Источник: http://greengerong.github.io/blog/2015/05/09/qian-duan-javascriptgui-fan