Перейти к содержимому

03.11.2010

7

5 пунктов о том, как хорошо писать плохой JavaScript

Вот такая забавная заметка о том, как писать плохой JavaScript. Была навеяна мне некоторым куском JS’а, который попался мне на глаза на днях. Данный пост не претендует на полноту и охват всего и вся, но тем не менее содержит некоторые примеры плохого кода на JavaScript.

Конечно в инете уже полно такого рода статей, но вот и я тоже решил внести свой вклад. В конце концов, чем больше будет таких статей, тем больше вероятность, что их кто-то прочитает и может быть сделает для себя выводы.
Вообще, мое личное мнение, что прежде чем начать делать какие-то практические вещи, надо все-таки иметь теоретические знания в данной предметной области. И если вы хотите писать на JavaScript, не надо думать, что если вы знаете, скажем, C++ или C# не дай бог, то значит вы знаете JavaScript. Это язык со своими особенностями и заморочками, и хорошо бы, прежде чем начать писать на нем, представлять себе как работает интерпретатор JS, как резолвятся переменные, что такое цепочки вызовов, как устроены замыкания, прототипы. На эти темы в сети полно материалов, я бы посоветовал просто в обязательном порядке посмотреть выступления (лучше все) Дугласа Крокфорда из Yahoo! Это просто Бог JavaScript’а. Каждый раз, когда смотрю его выступления, поражаюсь фундаментальности и системности мыслей этого человека. Его выступления можно найти в Yahoo UI Theatre. Там же можно найти материалы от другого, не менее интересного человека в области JavaScript, Николаса Закаса. Он является ведущим фронтэнд-разработчиком в Yahoo! и регулярно выступает на всех конфреренциях и рассказывает про оптимизацию фрондэнда и вообще много других интересных вещей. Кстати, к вопросу о переходе с других языков, есть отличная статья «How Good C# Habits can Encourage Bad JavaScript Habits» про то, как хорошие манеры в C# могут сослужить плохую службу при написании JS-кода, советую почитать. Так же есть, на мой взгляд, отличная статья JavaScript Closures от Ричарда Корнфорда , в которой описано как происходит резолвинг свойств объектов и переменных, что такое контекст выполнения и цепочки вызовов и конечно же что такое замыкания и с чем их едят 🙂

Такс, что-то я ушел в сторону, вернемся к теме.

Пункт номер раз. Делайте как можно больше глобальных переменных!

Зачем нам нужны локальные переменные! А вдруг какая-нибудь переменная нам понадобиться в другом месте? А ее нет. Не пойдет! Поэтому забываем про слово «var» и все наши переменные, не объявленные через var становятся глобальными переменными. Тогда и в случае выполнения какой-либо функции при обращении к переменной, интерпретатор сначала пойдет ее искать в локальных переменных данной функции — не найдет, потом перейдет к контексту выполнения, в случае если эта функция вызывалась не из глобального контекста, то интерпретатору придется пройтись по длинной цепочке вызовов, пока не дойдет до глобального и только там он найдет нашу переменную. Отлично!

Пункт номер два. Не надо кэшировать какие-либо объекты в локальные переменные!

Вообще этот пункт идет в противоречие с первым пунктом, так что его можно обощить до «Не надо вообще кэшировать DOM-объекты в переменные». Если вдруг вам в одной функции надо, скажем, много раз поманипулировать каким-нибудь DOM-объектом, ни в коем случае не делайте так:

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

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

Так вот если наша задача — замедлить приложение по максимум — не надо использовать цепочки вызовов, лучше делать каждый раз заново:

Пункт номер три (или два-точка-один). Не надо кэшировать результаты выполнения функций в локальные переменные.

Тут классический пример с массивами. Все знают про такое свойство массивов, как .length. Однако не многие знают, что хоть это и свойство массива, но на самом деле оно высчитывается заново при каждом обращении. Поэтому все циклы надо писать непременно вот так:

Пункт номер четыре. Не надо выбирать DOM-элеменыт по ID, лучше как можно конкретнее описать его со всех сторон.

Данный пункт более специфичен для фреймворков а-ля jQuery, но общий смысл вполне понятен. Пример кода выбора объектов на jQuery:

Хороший пример. Да, не смейтесь, я видел такой код не один раз. Ой, простите, я не вам. Что здесь написано? — Сначала выберем все элементы у которых задан CSS-класс «css_class1», затем среди них найдем все <div>, потом выберем всех его детишек первого уровня у кого id=obj_id, ну и затем тех из них, кто является элементом <div>. Все правильно! Мало ли где еще у нас в документе может быть элемент с id=obj_id. А уж тут мы точно выберем тот, который нам нужен.

Пункт номер пять. Не надо за один шаг делать ту работу, которую можно разбить на более мелкие шаги.

Может не очень хорошо сформулировано, но попробую объяснить, что я имею в виду. Самый простой пример для данного пункта — это динамическое выставление CSS-свойств для какого-либо DOM-элемента. Можно скопом менять значение всего element.style, а можно выставлять свойства по отдельности. Так вот, всегда выставляйте свойства по отдельности! Приведу пример кода на jQuery:

Ни в коем случае не вспоминайте про то, что в качестве параметра для $.css() можно передать объект с именованными свойствами, вот так:

И безусловно не надо использовать CSS-классы и просто переключать их, лучше делать так, как я показал выше, зашивать выставление css-свойств в код по очереди, иначе ваше приложение можно будет легко поддерживать и вносить правки в css, а так — каждый раз придется лезть в код, искать то место, где эти свойства выставляются и править.

Бонус. Пункт номер шесть. Ни в коем случае не надо выносить код, который используется несколько раз в отдельную фукнцию!

Данный пункт не специфичен для JavaScript, и подходит для всех языков программирования. Но вот почему-то вспомнилось. Так что, зачем придумывать какую-то новую функцию, писать ее объявление, потом вызывать отовсюду… Ведь есть CTRL+C/CTRL+V! Раз скопировал в буфер и вставил везде где надо! 5 секунд и все работает. И какая разница как потом поддерживать и мэйнтейнить этот код, главное что сейчас все работает!

Ну вот для начала и хватит. Если вдруг у кого есть какие замечания — велкам! Про дополнения не говорю, потому что эти пункты можно продолжать еще долго. И это наверное темы для других постов.

За сим откланиваюсь.

  • В пункте №4 узнал себя. Еще совсем недавно (где-то полгода назад) я так и делал 🙂

    По мне так статья отличная. Единственное, к чему хотелось бы сделать замечание, так это к заголовку. Не понятно, полезные советы даются или вредные. А ведь кто-то может воспринять это все как настоятельные рекомендации.

  • Хорошо, но мало примеров.
    И да, в пункте 4 неправильно описан алгоритм разбора jQuery селектора — он начинается с права налево, т.е. на примере div.classname вначале находится всё множество .classname потом из него берутся лишь те, что div (только в старых IE это работает не так, там нет метода getByClassName)

    • KSDaemon

      Про п.4 — поправил.
      Про мало примеров — понял, спасибо. Попробую дописать.

  • Хорошая статья. Все правильно написано.

    По замечаниям:
    — 5й пункт действительно «не очень хорошо сформулирован». По сравнению с остальными он очень «конкретный».
    — Вступление можно вообще убрать т.к. интересны сами пункты.

  • wik

    введение в виде аналогичных пунктов читалось бы веселей, мол не смотрите YUI Theater и не водитесь с Крокфордом и Закасом — эти парни научат вас только плохому 🙂

    • KSDaemon

      О! Кстати, да 🙂 Что-то я стормозил.

  • Меня больше удивил пункт номер 2, где сказано, что нельщя кешировать элементы страницы:) Только никто не учел, это может понижат скорость работы страницы, к тому же на телефонных устройствах это неприемлимо:)