Велосипеды на Javascript и jQuery

в 18:15, , рубрики: javascript, jquery

В очередной раз открыв код коллег и ужаснувшись, я решил написать эту статью. Надеюсь для кого-нибудь это будет полезным, заодно и мне будет проще новичкам объяснять что у них в коде не так, просто кинув ссылку на эту статью.
Безусловно количество таких вещей очень и очень велико, поэтому в статье ограничусь лишь некоторыми.

1. Константы в коде

Эта проблема касается не только javascript, а программирования в целом. Рассмотрим пример:

$elem.on('keydown', function(e) {
    if (e.keyCode == 27) {
        //...
    }
});

Что за магическое число 27? Люди, которые часто сталкиваются с кодами сразу скажут — это же клавиша ESC. Но большинство разработчиков, особенно начинающих, либо не помнят эти коды, либо не знают вообще, и сталкиваясь с кодами, вынуждены в очередной раз лезть в поисковую систему и тратить время.
Можно конечно добавить комментарий в коде, что это обработка нажатия клавиши ESC, но гораздо эффективнее было бы ввести константу, например, KEY_ESC = 27

2. Получение идентификаторов

Часто возникает необходимость получить идентификатор элемента (комментария, поста, пользователя и т.п.), чтобы выполнить какие-нибудь действия. (например, оценить комментарий при помощи ajax). И часто можно встретить подобный подход:

var id = $(this).attr('id').substring(8);

Как и в предыдущем примере разработчику приходится гадать — что же это за число 8. Лезть в html код и т.п.
Бывают примеры и хуже (строчка скопирована с реального проекта):

var last_id = $('#answer_pid' + id + ' li:first div').attr('id').substr(7);

Малейшее изменение верстки приведет к тому, что js код придется править.

Иногда бывает такое:

<div class="comment" id="comment_123"></div>

var id = $(this).attr('id').substring("comment_".length);

Уже лучше (по крайней мере нет вшитых чисел), но все равно данный подход слишком сильно привязывает js код к html.

На мой взгляд гораздо лучше использовать data-* параметры, например

<div class="comment" data-id="123"></div>

тогда получить идентификатор будет очень просто:

var id = $(this).attr('data-id');

или

var id = $(this).data('id');

(Про отличия в работе attr и data есть множество статей)

3. $.post

Как известно — в jquery есть метод для работы с ajax — $.ajax. К нему есть несколько shorthand функций, таких как $.get, $.load, $.post и т.п. Данные функции были добавлены специально, чтобы облегчить часто выполняемые действия (подгрузить скрипт, json, выполнить post запрос), но в реализации все эти методы ссылаются на $.ajax.
Лично я никогда не использую shorthand функции, и вот почему:

В коде начинающих или малоопытных разработчиков можно встретить несколько разных стадий:

1. Начальный

$.post(url, data, function(data) {
            data = $.parseJSON(data);
            //...
});

2. Добавляется try catch блок

$.post(url, data, function(data) {
	    try {
                data = $.parseJSON(data);
            } catch (e) {
                return;
            }
            //...
});

3. Узнаем из документации, что в $.post последним параметром можно передать dataType (который пропадает в безде кода, если success функция не влезает в экран).


$.post(url, data, function(data) {
    //...
}, 'json');

Очень редко веб разработчики добавляют обработчики ошибочных ситуаций. В основном это происходит из-за лени, либо нежелании потратить лишние 5 минут времени, либо разработчики просто уверены, что ошибок никогда не будет.
Если же разработчик решил к $.post добавить обработчик ошибок, то получается нечто вроде:

$.post(url, data, function(data) {
    //...
}, 'json').error(function() {
   ///
});

На мой взгляд — это ужасно нечитабельно. Да и писать каждый раз обработчик ошибок — дело нудное, поэтому можно настроить обработчик ошибок по умолчанию для всех ajax запросов, например:


$.ajaxSetup({
    error: function() {
        //Показать окошко о том, что произошла ошибка
    }
});

Вернемся к $.post. Как показано выше — использование $.post делает код ужасным (особенно с dataType в непонятном месте). Перепишем последний пример на $.ajax. На мой взгляд данный подход читабельнее и проще в поддержке.


$.ajax({
    type: "POST"
    url: url,
    data: data,
    dataType: "json",
    success: function(data) {
        //
    },
    error: function() {
        //
    }
})l

4. Обработчики событий к нескольким элементам

Часто бывает необходимость добавить обработчики событий к элементам страницы (например, кнопка «удалить сообщение»). И зачастую можно встретить подобный подход:


$('.comment a.delete').click(function(){
    //
});

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


$('.comment a.delete').unbind('click').click(function() {
    //
});

Решение: в jQuery 1.7 есть метод on, который привязывает обработчики события, фильтруя элементы по селектору.
пример:


$('body').on('click', 'a.external', function(e) {
	//функция будет вызвана при клике на любую ссылку с классом external
});

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


$('body').on('mousemove', selector, function() {
	//
});

5. Namespaced events

Несмотря на то, что namespaced events были добавлены в jQuery 1.2 — ими мало кто пользуется (Мне кажется большинство людей просто не знают о них).
Рассмотрим пример:


$('a').on('click', function() {
	//обработчик 1
});
$('a').on('click', function() {
	//обработчик 2
});

Теперь предположим, что нам нужно удалить второй обработчик от ссылок. Но вот ведь незадача — $('a').off('click') удалит оба обработчика. На помощь приходит namespaced events. Перепишем код выше:


$('a').on('click.namespace1', function() {
	//обработчик 1
});
$('a').on('click.namespace2', function() {
	//обработчик 2
});

Теперь становится возможным удалить второй обработчик при помощи вызова $('a').off('click.namespace2');
Подробнее о namespaced events можно прочитать здесь: docs.jquery.com/Namespaced_Events

Вместо заключения

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

Автор: Sirian

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js