Codex

Interested in functions, hooks, classes, or methods? Check out the new WordPress Code Reference!

Стандарты кодирования Javascript

JavaScript стал важным звеном в развитии WordPress-приложений (тем и плагинов), а также ядра WordPress. Для форматирования и стилизации кода JavaScript необходимы стандарты, чтобы сохранить ту же согласованность, которую стандарты кодирования WordPress обеспечивают для PHP, HTML и CSS кода.

"Весь код должен выглядеть так, как будто он написан одним человеком, независимо от того, сколько людей участвовали в его написании." 

Cтандарты кодирования JavaScript для WordPress развиты на основе jQuery JavaScript Style Guide. Наш стандарт отличается от руководства для jQuery в следующих моментах:

  • WordPress использует одинарные кавычки для строк.
  • Директивы case сдвигаются на отступ внутри блоков switch.
  • Тело функции всегда оформляется отступом.
  • Есть некоторые отличия в использовании пробелов для согласованности со стандартами кодирования PHP.
  • 100-символьный лимит jQuery приветствуется, но не является строго обязательным.

Многие примеры, приведенные ниже, были адаптированы непосредственно из руководства по стилю кодирования jQuery; указанные выше различия были интегрированы в примеры на этой странице. Приведенные ниже стандарты и примеры должны рассматриваться как лучшие образцы кода для WordPress, если особо не оговорено, что это анти-пример.

Рефакторинг кода

"Рефакторинг кода не должен делаться только потому, что мы можем это сделать." - Ведущий Разработчик Andrew Nacin

Многие части JavaScript-кода WordPress не согласованы по стилю. WordPress работает над улучшением ситуации, с тем чтобы код становился чище и читабельнее.

Хотя стандарты кодирования важны, рефакторинг старых .js-файлов только для того, чтобы они соответствовали нормам, не является первостепенной задачей. Чисто стилевые патчи для старых файлов настоятельно не рекомендуются.

Весь новый или обновленный JavaScript-код будет проверяться на соответствие стандартам и тестироваться JSHint.

Интервалы

Активно используйте пробелы в вашем коде. "Если сомневаетесь, ставьте пробел."

Эти правила рекомендуют максимальное использование интервалов для улучшения читабельности. С помощью минимизации можно создать файл, который оптимизирован для чтения и обработки браузерами.

  • Оформляйте отступы с помощью табуляций.
  • Не используйте пробелы в конце строк и в пустых строках.
  • Строки обычно пишутся не длиннее 80 символов и определенно не должны превышать 100 (считая каждую табуляцию за 4 пробела). Это нежесткое правило, но длинные строки обычно указывают на нечитабельный или плохо организованный код.
  • if/else/for/while/try блоки должны всегда использовать фигурные скобки и располагаться на нескольких строках.
  • Унарные операторы (например, ++, --) не должны иметь пробелов после своих операндов.
  • Перед , и ; не должно быть пробелов.
  • Любая ; как признак конца команды должна быть в конце строки.
  • Перед : после имени свойства в определении объекта не должно быть пробелов.
  • Знаки ? и : в тернарном условном операторе должны иметь пробелы с обеих сторон.
  • В пустых конструкциях (например, {}, [], fn()) не должно быть заполняющих пробелов.
  • Должна быть новая строка в конце каждого файла.
  • За знаком оператора отрицания ! должен идти пробел.
  • Тело функции оформляется отступом в одну табуляцию.
  • Пробелы могут использоваться для выравнивания кода в документированных блоках или в строках, но для отступов в начале строки должны использоваться только табуляции.

Стандарты JavaScript-кода в WordPress предполагают несколько более широкое использование пробелов, чем в стандартах jQuery. Эти изменения приняты для согласованности между PHP и JavaScript-кодом в WordPress.

Пробелы могут легко накапливаться в конце строки - избегайте этого, так как конечные пробелы будут обнаруживаться JSHint как ошибка. Один из способов контролировать наращивание пробелов - сделать пробелы видимыми в вашем текстовом редакторе.

Объекты

Объявление объекта может располагаться на одной строке, если оно достаточно короткое (вспомните о рекомендациях по поводу длины строк). Если объявление объекта слишком длинное и не помещается на одной строке, тогда каждое свойство должно располагаться на отдельной строке. Имена свойств должны заключаться в кавычки только, если они являются зарезервированными словами или содержат специальные символы:

// Предпочтительно
var map = {
    ready: 9,
    when: 4,
    'you are': 15
};
 
// Приемлемо для небольших объектов
var map = { ready: 9, when: 4, 'you are': 15 };
 
// Плохо
var map = { ready: 9,
    when: 4, 'you are': 15 };

Массивы и вызовы функций

Всегда ставьте дополнительные пробелы вокруг элементов и аргументов:

array = [ a, b ];
 
foo( arg );
 
foo( 'string', object );
 
foo( options, object[ property ] );
 
foo( node, 'property', 2 );

Исключения:

// Для обеспечения согласованности с PHP-стандартами не ставьте пробелов вокруг
// строковых литералов или целых чисел, используемых в качестве ключей в массивах
prop = object['default'];
firstArrayElement = arr[0];
 
// Функция, единственным аргументом которой является callback-функция, объект или массив
// По обе стороны аргумента пробелов быть не должно
foo(function() {
    // Do stuff
});
 
foo({
    a: "Альфа",
    b: 'бета'
});
 
foo([
    "Альфа",
    'бета'
]);
 
// Если первым аргументом функции является callback-функция, объект или массив,
// то перед первым аргументом не должно быть пробелов 
foo(function() {
    // Do stuff
}, options );
 
// Если последним аргументом функции является callback-функция, объект или массив,
// то после последнего аргумента не должно быть пробелов 
foo( data, function() {
    // Do stuff
});

Примеры правильной расстановки пробелов

var i;
 
if ( condition ) {
    doSomething( 'with a string' );
} else if ( otherCondition ) {
    otherThing({
        key: value,
        otherKey: otherValue
    });
} else {
    somethingElse( true );
}
 
// В отличие от jQuery в WordPress после оператора ! должен идти пробел. 
// Это сделано также для согласованности с PHP-стандартами.
while ( ! condition ) {
    iterating++;
}
 
for ( i = 0; i < 100; i++ ) {
    object[ array[ i ] ] = someFn( i );
    $( '.container' ).val( array[ i ] );
}
 
try {
    // Выражения
} catch ( e ) {
    // Выражения
}

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

Используйте их. Никогда не полагайтесь на автоматическую вставку (ASI).

Отступы и переносы строк

Отступы и переносы строк добавляют читабельности для сложных конструкций.

Для отступов должны использоваться табуляции. Даже если весь файл содержится в одной замкнутой структуре (например, в функции), содержимое функции должно иметь отступ в одну табуляцию:

(function( $ ) {
    // Выражения с отступом
 
    function doSomething() {
        // Выражения с отступом
    }
})( jQuery );

Блоки и фигурные скобки

if, else, for, while, try - блоки должны всегда использовать фигурные скобки, и всегда располагаться на нескольких строках. Открывающая скобка должна быть в одной строке с определением функции, условием или началом цикла. Закрывающая скобка должна быть в строке, непосредственно следующей за последним оператором блока.

var a, b, c;
 
if ( myFunction() ) {
    // Выражения
} else if ( ( a && b ) || c ) {
    // Выражения
} else {
    // Выражения
}

Многострочные предложения

Когда предложение слишком длинное для одной строки, переносы строк должны вставляться после операторов.

// Плохо
var html = '<p>The sum of ' + a + ' and ' + b + ' plus ' + c
    + ' is ' + ( a + b + c );
 
// Хорошо
var html = '<p>The sum of ' + a + ' and ' + b + ' plus ' + c +
    ' is ' + ( a + b + c );

Строки должны разбиваться на логические группы, если это улучшает читабельность, например, каждое выражение тернарного оператора лучше помещать на свою строку, даже если оба помещаются на одной строке:

// Приемлемо
var baz = ( true === conditionalStatement() ) ? 'thing 1' : 'thing 2';
 
// Лучше
var baz = firstCondition( foo ) && secondCondition( bar ) ?
    qux( foo, bar ) :
    foo;

Когда условние слишком длинное и не помещается на одной строке, последующие строки должны быть с отступом на один дополнительный уровень, чтобы отличить их от основного кода.

if ( firstCondition() && secondCondition() &&
        thirdCondition() ) {
    doStuff();
}

Цепочка вызовов методов

Если цепочка вызовов методов слишком длинная и не помещается на одной строке, каждый вызов метода должен быть на отдельной строке, а первый вызов должен располагаться на следующей строке после имени объекта, методы которого вазываются. Если метод изменяет контекст, дополнительный отступ должен быть использован.

elements
    .addClass( 'foo' )
    .children()
        .html( 'hello' )
    .end()
    .appendTo( 'body' );

Присваивания и глобальные переменные

Объявление переменных с помощью var

Каждая функция должна начинаться с объявления переменных с помощью ключевого слова var, за которым через запятую следуют необходимые локальные переменные. Если переменная не объявляется с помощью var, она может проникнуть во внешнюю область (в худшем случае, в глобальную область видимости) и невольно привести к модификации данных.

Присваивания в пределах var должны располагаться на отдельных строках, в то время как объявления могут быть сгруппированы на одной строке. Каждая дополнительная строка должна иметь дополнительный отступ. Присваивания для объектов и функций, занимающие больше, чем несколько строк, должны быть вынесены за пределы var, чтобы избежать нагромождения отступов.

// Хорошо
var k, m, length,
    // Дополнительная строка сдвинута на один отступ
    value = 'WordPress';
 
// Плохо
var foo = true;
var bar = false;
var a;
var b;
var c;

Глобальные переменные

В прошлом код WordPress затруднял использование глобальных переменных. Поскольку JavaScript-файлы ядра иногда используются в плагинах, существующие глобальные переменные не должны удаляться. Все глобальные переменные, используемые в файле, должны быть задокументированы в верхней части этого файла. Несколько глобальных переменных можно перечислить через запятую. Этот пример делает passwordStrength разрешенной глобальной переменной внутри файла:

/* global passwordStrength:true */

true после passwordStrength означает, что эта глобальная переменная определяется в пределах этого файла. Если вы обращаетесь к глобальной переменной, определенной в другом месте, опустите :true, чтобы указать, что глобальная переменная используется только для чтения.

Распространенные библиотеки

Backbone, jQuery, Underscore и глобальный объект wp зарегистрированы как разрешенные глобальные переменные в корневом .jshintrc - файле. К Backbone и Underscore можно обратиться непосредственно в любом месте. jQuery доступна через $ при передаче jQuery-объекта в анонимную функцию:

(function( $ ) {
  // Выражения
})( jQuery );

Это позволяет избежать необходимости вызова .noConflict() или установки псевдонима с помощью другой переменной. Файлы, которые добавляют или изменяют объект wp, должны безопасно обращаться к глобальному объекту, чтобы избежать перезаписи ранее установленных свойств:

// В начале файла присвойте "wp" существующее значение (если оно есть)
window.wp = window.wp || {};

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

Переменных и имена функций должны быть целыми словами. Используйте camelCase-нотацию со строчной первой буквой. Это область, где этот стандарт отличается от стандартов кодирования PHP в WordPress .

Конструкторы, предназначенные для использования с new, должны иметь прописную первую букву (UpperCamelCase).

Имена должны быть понятными, но не избыточными. Исключения допускаются для итераторов, например, использование i в качестве счетчика в цикле.

Комментарии

Комментарии должны предшествовать коду, к которому они относятся. Перед комментарием должна идти пустая строка. Начинайте комментарий с прописной буквы. Ставьте точку в конце, если комментарий является законченным предложением. Между символом комментария (//) и текстом комментария должен быть один пробел.

Однострочный комментарий:

someStatement();
 
// Объяснение чего-то сложного в следующей строке
$( 'p' ).doSomething();

Для длинных комментариев используйте многострочные комментарии:

/*
Это достаточно длинный комментарий, и его
можно разбить на несколько строк.
*/

Встроенные комментарии допускаются как исключение для аннотирования специальных аргументов в списке формальных параметров:

function foo( types, selector, data, fn, /* INTERNAL */ one ) {
    // Делать что-то
}

Равенство

Строгая проверка на равенство (===) должна использоваться вместо простой проверки (==). Единственным исключением является ситуация, когда осуществляется проверка на undefined или null путем проверки на равенство null.

// Проверка undefined и null-значений по каким-то важным причинам.
if ( undefOrNull == null ) {
    // Выражения
}

Проверка типа

Ниже приведены предпочтительные способы проверить тип объекта:

  • Строка: typeof object === 'string'
  • Число: typeof object === 'number'
  • Логический тип: typeof object === 'boolean'
  • Объект: typeof object === 'object' или _.isObject( object )
  • Простой объект: jQuery.isPlainObject( object )
  • Функция: _.isFunction( object) или jQuery.isFunction( object )
  • Массив: _.isArray( object ) или jQuery.isArray( object )
  • Элемент: object.nodeType или _.isElement( object )
  • null: object === null
  • null или undefined: object == null
  • undefined:
    • Глобальная переменная: typeof variable === 'undefined'
    • Локальная переменная: variable === undefined
    • Свойство: object.prop === undefined
    • Любое из вышеперечисленных: _.isUndefined( object )

Всюду, где используется Backbone или Underscore, рекомендуется использовать проверку типов Underscore.js вместо jQuery.

Строки

Используйте одинарные кавычки для строковых литералов:

var myStr = 'строки должны заключаться в одинарные кавычки';

Когда строка содержит одинарные кавычки, они должны быть экранированы символами обратной косой черты (\):

// Экранируйте одиночные кавычки внутри строки:
'Note the backslash before the \'single quotes\'';

Switch

Использование конструкции switch, как правило, не рекомендуется, но может быть полезно, когда существует большое количество вариантов case, особенно если несколько вариантов могут быть обработаны в одном блоке, или вариант default может быть использован.

При использовании конструкции switch:

  • Используйте break; для каждого варианта case, отличного от default. При группировке case отмечайте это явным образом.
  • Сдвигайте варианты case на одну табуляцию по сравнению со switch.
switch ( event.keyCode ) {
 
    // ENTER и SPACE оба вызывают x()
    case $.ui.keyCode.ENTER:
    case $.ui.keyCode.SPACE:
        x();
        break;
    case $.ui.keyCode.ESCAPE:
        y();
        break;
    default:
        z();
}

Не рекомендуется возвращать значение из оператора switch: используйте блоки case, чтобы сохранить значение, а затем в конце верните значение с помощью return.

function getKeyCode( keyCode ) {
    var result;
 
    switch ( event.keyCode ) {
        case $.ui.keyCode.ENTER:
        case $.ui.keyCode.SPACE:
            result = 'commit';
            break;
        case $.ui.keyCode.ESCAPE:
            result = 'exit';
            break;
        default:
            result = 'default';
    }
 
    return result;
}

Практические рекомендации

Массивы

Массивы в JavaScript следует создавать, используя сокращенный конструктор [], а не вызовом new Array().

var myArray = [];

Вы можете инициализировать массив при создании:

var myArray = [ 1, 'WordPress', 2, 'Blog' ];

В JavaScript ассоциативные массивы определяются как объекты.

Объекты

Существует много способов создания объектов в JavaScript. Литеральная нотация {} является как наиболее удобной, так и более читабельной.

var myObj = {};

Литеральная нотация объекта должна использоваться, если объект не требует прототипа. Иначе объект должен быть создан с помощью вызова конструктора new.

var myObj = new ConstructorMethod();

Свойства объекта должны быть доступны через точечную нотацию, если ключ не является переменной, зарезервированным словом или строкой, которая не может быть идентификатором:

prop = object.propertyName;
prop = object[ variableKey ];
prop = object['default'];
prop = object['key-with-hyphens'];

Условия Йоды

Для совместимости со стандартами для PHP-кода когда вы сравниваете объект c величиной типа string, boolean, integer или другой постоянной или литералом, переменная всегда должна располагаться справа, а постоянная или литерал - слева.

if ( true === myCondition ) {
    // Делать что-то
}

Такую запись немного странно читать. Но делайте так и вы привыкнете.

Перебор

При переборе большой коллекции с помощью цикла for, рекомендуется хранить максимальное значение счетчика в заранее вычисленной переменной вместо повторного вычисления максимального значения каждый раз:

// Хорошо и эффективно
var i, max;
 
// getItemCount() вызывается однократно
for ( i = 0, max = getItemCount(); i < max; i++ ) {
    // Делать что-то
}
 
// Плохо и неэффективно:
// getItemCount() вызывается каждый раз
for ( i = 0; i < getItemCount(); i++ ) {
    // Делать что-то
}

Функции Underscore.js для коллекций

Изучите методы Underscore для коллекций и массивов. Эти функции, в том числе _.each, _.map , _.reduce, позволяют эффективно и наглядно преобразовывать большие наборы данных.

Underscore также допускает написание цепочек объектов JavaScript в стиле Query:

var obj = {
    first: 'thing 1',
    second: 'thing 2',
    third: 'lox'
};
 
var arr = _.chain( obj )
    .keys()
    .map(function( key ) {
        return key + ' comes ' + obj[ key ];
    })
    // Окончание цепочки
    .value();

// arr === [ 'first comes thing 1', 'second comes thing 2', 'third comes lox' ]

Перебор коллекции jQuery

Единственный случай, когда jQuery должен использоваться для перебора, - это работа с коллекцией jQuery-объектов:

$tabs.each(function( index, element ) {
    var $element = $( element );

    // Делать что-то с $element
});

Никогда не используйте jQuery, чтобы перебирать исходные данные или обычные объекты JavaScript.

JSHint

JSHint - это инструмент для автоматизированной проверки кода, предназначенный для обнаружения ошибок в коде JavaScript. JSHint используется в разработке WordPress для того, чтобы быстро убедиться, что патч не приведет к логическим или синтаксическим ошибкам в клиентских скриптах.

Инсталляция и запуск JSHint

JSHint запускается с помощью инструмента Grunt. Как JSHint, так и Grunt являются программами, написанными в Node.js. Конфигурационный файл, который поставляется с кодом WordPress, позволяет легко установить и настроить эти инструменты.

Чтобы установить Node.js, щелкните ссылку "Install" на сайте Node. Загрузится файл для вашей операционной системы. Выполните все шаги установки для вашей ОС.

Как только Node.js установится, откройте окно командной строки и перейдите в каталог, где вы проверяете копию SVN-репозитория WordPress (используйте cd ~/directoryname). Вы должны быть в корневом каталоге, который содержит файл package.json.

Далее введите npm install в командной строке. Будут скачаны и установлены все пакеты Node, которые используются в разработке WordPress.

Наберите npm install -g grunt-cli, чтобы установить интерфейс командной строки ( Command Line Interface, CLI). Grunt CLI используется, чтобы запускать задачи Grunt в WordPress.

Теперь вы можете ввести grunt jshint, чтобы выполнить Grunt-проверку всех JavaScript-файлов WordPress'а на синтаксические и логические ошибки. Чтобы проверить только код ядра (core), наберите grunt jshint:core, чтобы проверить только js - файлы тестов, введите grunt jshint:tests.

Настройки JSHint

Конфигурационные параметры JSHint хранятся в .jshintrc-файле в SVN-репозитории WordPress. Этот файл определяет, какие ошибки JSHint должен фиксировать, если найдет их в исходном коде WordPress.

Проверка отдельного файла

Чтобы проверить отдельный файл, добавьте --file=filename.js в конец команды. Например, чтобы проверить только файл "admin-bar.js" из ядра WordPress, введите:

grunt jshint:core --file=admin-bar.js

А эта команда проверит только файл "password-strength-meter.js" в директории тестов:

grunt jshint:tests --file=password-strength-meter.js

Использование JSHint для проверки отдельных файлов может быть полезно, если вы работаете только с одним-двумя конкретными файлами и не хотите ждать, пока JSHint будет проверять все файлы при каждом запуске.

Переопределение JSHint: исключение блоков

В некоторых ситуациях части файла должны быть исключены из проверки JSHint. Например, скрипт для административной панели содержит сжатый код jQuery-плагина HoverIntent, - это сторонний код, который не должен проходить через JSHint, хотя он и является частью JavaScript-файла WordPress.

Для исключения определенной части кода из проверки JSHint, заключите его в директивные JSHint-комментарии:

/* jshint ignore:start */
if ( typeof jQuery.fn.hoverIntent === 'undefined' ) {
    // hoverIntent r6 - Copy of wp-includes/js/hoverIntent.min.js
    (function(a){a.fn.hoverIntent=...............
}
/* jshint ignore:end */

Источники

Примеры jQuery взяты из jQuery JavaScript Style Guide, который доступен по лицензии MIT.