Я хочу описать простой минималистский подход к разработке сложных JavaScript приложений. Из внешних библиотек будут использоваться только jQuery и мой js-шаблонизатор, причём из jQuery используются только $.ready() , $.ajax() и $.proxy() — т.е. суть не в библиотеках (их тривиально заменить на предпочитаемые вами), а в самом подходе.
- JavaScript виджеты — небольшие модули, каждый из которых «владеет» определённой частью веб-странички (т.е. всё управление этой частью странички происходит исключительно через методы этого модуля, а не через прямую модификацию DOM — инкапсуляция). Виджет отвечает исключительно за функциональность, но не за внешний вид; поэтому прямая модификация части DOM, которым «владеет» виджет, снаружи виджета допускается — но только для чисто дизайнерских задач (для архитектуры и общей сложности приложения нет принципиальной разницы между коррекцией внешнего вида через CSS или jQuery).
- Глобальный диспетчер событий. Взаимодействие между виджетами осуществляется путём посылки сообщений глобальному диспетчеру (слабая связанность, паттерн Mediator/Посредник), а уже он принимает решение что с этим сообщением делать — создать/удалить виджеты, дёрнуть методы других виджетов, выполнить дизайнерский код, etc. В отличие от динамического подхода к обработке событий (когда обработчики конкретного события добавляются/удаляются в процессе работы) статический диспетчер сильно упрощает понимание и отладку кода. Безусловно, есть задачи, для которых нужны именно динамические обработчики событий, но в большинстве случаев это избыточное усложнение, поэтому всё, что можно, делается статическими обработчиками.
О велосипедах
Я обычно разрабатываю серверную часть приложения (причём больше даже не столько сам веб-сайт, сколько сетевые сервисы), клиентская часть это не мой основной профиль. Так что когда у меня впервые возникла задача разработки достаточно сложного одностраничного веб-приложения (примерно два года назад), я поискал готовые архитектурные подходы для таких приложений, но, к сожалению, не нашёл (хотя к тому моменту уже прошло пол года с момента доклада Nicholas Zakas «Scalable JavaScript Application Architecture»: видео, слайды).
Почему начинающие не понимают Javascript
Разработанную мной тогда архитектуру я обкатывал эти два года на разных проектах, проверял как она работает на реальных задачах. Хорошо работает. 🙂 Поэтому я и решил написать эту статью. В процессе написания ещё раз поискал другие решения, и обнаружил доклад Nicholas Zakas, плюс ещё из прочитанного на эту тему очень понравилась статья Addy Osmani Patterns For Large-Scale JavaScript Application Architecture.
- На мой взгляд, та архитектура необходима для приложений масштаба Yahoo!, GMail, etc. но для абсолютного большинства современных достаточно сложных веб-приложений это всё-таки перебор. Избыточная гибкость этой архитектуры имеет свою цену — она увеличивает сложность приложения. Вот пример фич, в которых у меня никогда не возникало реальной необходимости:
Как выучить JavaScript? Самый аху#### способ!
- возможность просто заменять базовую библиотеку (напр. jQuery на Dojo)
- проверка и контроль «прав доступа» модулей к функциональности системы
- динамическое добавление/удаление модулей на странице
- по поводу динамических обработчиков событий я уже писал выше
- Как следствие разницы в масштабе функциональности модулей, у них модули состоят из HTML+CSS+JavaScript, а у меня большинство модулей это только JavaScript плюс документация на минимально необходимую этому модулю структуру HTML.
Начало
Создаём скелет странички нашего приложения.
Простой минималистский пример сложного JavaScript приложения /* Основной код приложения */
Добавляем глобальный диспетчер событий. События представляют из себя текстовое имя события и не обязательный хеш с параметрами.
Обычно после загрузки страницы нам необходимо выполнить какие-то действия (как минимум создать и добавить на страницу виджеты), и для единообразия мы это событие тоже обработаем в основном диспетчере.
/* Основной код приложения */ notice = (function()< var w = < >; return function(e,data) < switch (e) < case ‘document ready’: break; default: alert(‘notice: unknown event «‘+e+'»‘); >>; >)(); $(document).ready(function()< notice(‘document ready’) >);
Хеш w будет использоваться для хранения создаваемых объектов-виджетов — надо же их где-то хранить после создания — плюс чтобы у диспетчера (который и будет эти объекты создавать) всегда был к ним доступ.
А что мы делаем-то?
Универсальная часть готова, самое время определиться что же наше приложение будет делать. Чтобы продемонстрировать сложное динамическое взаимодействие между разными частями странички нам нужно будет несколько активных элементов, действия которых будут отражаться в разных частях странички.
Давайте сделаем приложение, в которое можно будет добавлять/удалять фразы, а оно будет показывать количество каких-нибудь результатов по этим фразам (например количество ошибок найденное сервисом проверки правописания). Плюс суммарное количество результатов по всем фразам.
Архитектура
- AddPhrase — виджет добавления фразы.
- Методы: нет.
- Генерирует события: ‘add phrase’ .
- Методы: add_handler .
- Генерирует события: ‘select phrase’ .
- Методы: нет.
- Генерирует события ‘spellcheck: started’ и ‘spellcheck: success’ .
- Методы: add и sub .
- Генерирует события: нет.
Виджеты
Хотя виджет владеет своей областью, он не контролирует где именно эта область находится на страничке — т.е. он не знает, видна ли она на экране, да и вообще добавлена ли в DOM, или нет. За добавление/удаление контролируемых виджетами областей на страничку отвечает внешний код (т.е. наш глобальный диспетчер событий, либо внешний виджет в случае когда одни виджеты вложены в другие).
- глобальная функция notice() для генерации событий;
- объект jQuery $ ;
- функции библиотеки-шаблонизатора.
- Виджеты находятся в отдельных файлах w.widget_name.js.
- Виджеты реализованы как классы в глобальном namespace W , обычно один виджет — один класс: W.WidgetName .
- Классы реализованы в самом простом и естественном для JavaScript виде — обычные функции-конструкторы и прототипное наследование.
- Приватные свойства и методы начинаются на подчёркивание.
- В начале виджета документируется какой ему нужен формат js-шаблона (или блока HTML); конструктор, публичные методы/свойства, генерируемые события.
- По соглашению, контролируемая виджетом область доступна через свойство объекта-виджета .$ (именно его внешний код вставляет в DOM чтобы вывести виджет на страничке).
AddPhrase
Требования этого виджета к HTML — нужна форма с одним input type=text. Всё остальное дизайнер может оформлять как угодно.
/* * * * w = new W.AddPhrase(‘addphrase’); * * -> ‘add phrase’, phrase * */ var W; W = W || <>; W.AddPhrase = function(id)< this.$ = $( ‘#’+id ); this._input = this.$.find(‘input’); // cache this.$.submit( $.proxy(this,’_onsubmit’) ); >; W.AddPhrase.prototype._onsubmit = function()< notice(‘add phrase’, < phrase: this._input.val() >); this._input.val(»); return false; >;
Добавляем создание виджета при загрузке страницы. И немного кода для юзабилити — такой код должен быть именно здесь, а не в виджете, т.к. виджет не знает что в данном приложении именно он — главный элемент интерфейса и с него начинается работа.
. Добавить фразу:
/* Основной код приложения */ notice = (function()< var w = < addphrase: null >; return function(e,data)< switch (e) < case ‘document ready’: w.addphrase = new W.AddPhrase(‘addphrase’); $(‘#addphrase input’).focus(); break; case ‘add phrase’: break;
Собственно, это всё. 🙂 У нас есть универсальный виджет, который можно «повесить» на любые формы, и который исправно генерирует события по мере ввода пользователем фраз (мы их пока в диспетчере игнорируем, т.к. ещё нет виджета, который должен их обрабатывать).
При этом в виджете нет ни одной «капли жира» — строчки, не относящейся к его непосредственной функциональности. В каком стиле бы не реализовалась эта функциональность, все эти строчки будут в любом случае. В диспетчере, конечно, «лишние» строчки есть — если сравнивать с не модульным приложением в стиле N-летней давности, когда весь код писался одним куском. Но на самом деле эти «лишние» строчки — самое ценное в нашем приложении, т.к. именно они наглядно и просто описывают высокоуровневую логику приложения.
Phrase
На примере этого виджета мы посмотрим на работу с js-шаблонами и вложенными виджетами.
Требования этого виджета к HTML — нужен js-шаблон, в который будет передана переменная phrase , и в котором должен быть элемент с классом handlers — в него будут добавляться виджеты с результатами.
/* * * * * * * * * w = new W.Phrase(‘tmpl_phrase’, ‘some phrase’); * w.add_handler( new W.SomeHandler() ); * * -> ‘select phrase’, phrase * */ var W; W = W || <>; W.Phrase = function(tmpl, phrase)< this.$ = $( POWER.render(tmpl, < ‘phrase’: phrase >) ); this._phrase= phrase; this._w = []; this._h = this.$.find(‘.handlers’); // cache this.$.click( $.proxy(this,’_onclick’) ); >; W.Phrase.prototype.add_handler = function(w_handler)< this._w.push( w_handler ); this._h.append( w_handler.$ ); >; W.Phrase.prototype._onclick = function()< notice(‘select phrase’, < phrase: this._phrase >); >;
С шаблонами думаю, всё понятно. А вот работу с вложенными виджетами лучше пояснить.
Хоть виджет проверки правописания «вложен» визуально в виджет Phrase и виджет Phrase «владеет» виджетом проверки правописания (единственная ссылка на виджет проверки правописания находится в this._w , в то время как все глобальные виджеты удерживаются ссылками в хеше w ), тем не менее, как вы видите, Phrase не создаёт объект виджета проверки правописания, а получает его параметром. Это внедрение зависимости (dependency injection) для увеличения гибкости — позволяет избежать зависимости между конкретными виджетами и упрощает создание объектов виджетов: дело в том, что обычно все необходимые данные для вызова конструктора вложенного виджета есть только у диспетчера, и такой подход позволяет избежать прозрачной передачи этих параметров для конструкторов вложенных виджетов через конструкторы внешних виджетов.
/* Основной код приложения */ notice = (function() < var w = < . phrase: <>>; return function(e,data)< . case ‘add phrase’: if(data.phrase in w.phrase) break; w.phrase[data.phrase] = new W.Phrase(‘tmpl_phrase’, data.phrase); $(‘#phrases’).append( w.phrase[data.phrase].$ ); break; case ‘select phrase’: w.phrase[data.phrase].$.remove(); delete w.phrase[data.phrase]; $(‘#addphrase input’).focus(); break;
Пара пояснений: во-первых у нас ещё нет виджета W.SpellCheck, поэтому мы пока не вызываем w.phrase[phrase].add_handler( new W.SpellCheck() ) ; во-вторых после клика на виджете Phrase (для удаления фразы) теряет фокус строка ввода фразы, и его надо вернуть.
SpellCheck
На примере этого виджета мы посмотрим на работу с ajax.
Кроме того нам нужно будет показывать состояние ajax-запроса (в процессе, получен ответ). Есть два разных подхода: можно изменять DOM в диспетчере (обрабатывая события ‘spellcheck: started’ и ‘spellcheck: success’ ), а можно прошить все состояния внутри js-шаблона и многократно выполнять/подменять шаблон. Второй способ немного сложнее, но т.к. у нас пример, то стоит реализовать именно его.
Для его реализации приходится использовать один трюк. Дело в том, что если мы сохраним выполненный шаблон в свойство .$ (как в виджете Phrase), то мы в дальнейшем уже не сможем его подменить — дело в том, что значение .$ будет добавлено куда-то в DOM (мы внутри виджета не знаем куда), и если мы просто заменим значение в .$ новым шаблоном, то в DOM останется ссылка на старое значение .$ и визуально на страничке ничего не изменится (а этот виджет окончательно потеряет доступ к «своей» части странички). Чтобы этого избежать в .$ сохраняется любой пустой тэг-контейнер (напр. span или div), а уже в него помещается результат выполнения шаблона. К сожалению, при этом на страничке появляется «неожиданный» для дизайнера тэг, но как эту проблему решить более элегантно я не придумал.
Требования этого виджета к HTML — нужен js-шаблон, в который будут переданы переменные status (возможные значения «started» и «success» ) и spellerrors (если значение status==»success» ).
w.spellcheck.js
/* * * * Проверяю… * else < %>* Найдено ошибок. * %> * * * w = new W.SpellCheck(‘tmpl_spellcheck’, ‘some phrase’); * * -> ‘spellcheck: started’, phrase * -> ‘spellcheck: success’, phrase, spellerrors * */ var W; W = W || <>; W.SpellCheck = function(tmpl, phrase)< this.$ = $(»); this._tmpl = tmpl; this._phrase= phrase; this._load(); >; W.SpellCheck.prototype._load = function()< $.ajax(< url: ‘http://speller.yandex.net/services/spellservice.json/checkText’, data: < text: this._phrase >, dataType: ‘jsonp’, success: $.proxy(this,’_load_success’) >); this.$.html( POWER.render(this._tmpl, < status: ‘started’ >) ); notice(‘spellcheck: started’, < phrase: this._phrase >); > W.SpellCheck.prototype._load_success = function(data)< this.$.html( POWER.render(this._tmpl, < status: ‘success’, spellerrors: data.length >) ); notice(‘spellcheck: success’, < phrase: this._phrase, spellerrors: data.length >); >;
. . . . (проверяю…) else < %>(найдено ошибок) %>
case ‘add phrase’: . w.phrase[data.phrase].add_handler(new W.SpellCheck(‘tmpl_spellcheck’, data.phrase)); . break; case ‘spellcheck: started’: break; case ‘spellcheck: success’: break;
Sum
/* * * * w = new W.Sum(‘sum’); * w.add(5); * w.sub(3); * */ var W; W = W || <>; W.Sum = function(id)< this.$ = $( ‘#’+id ); this.$.html(0); >; W.Sum.prototype.add = function(n)< this.$.html( parseInt(this.$.html()) + n ); >; W.Sum.prototype.sub = function(n)< this.$.html( parseInt(this.$.html()) — n ); >;
. . .
Итого: ошибок.
/* Основной код приложения */ notice = (function()< var w = < . sum: null >; var spellerrors = <>; return function(e,data) < . case ‘document ready’: . w.sum = new W.Sum(‘sum’); break; case ‘select phrase’: . if(data.phrase in spellerrors)< w.sum.sub(spellerrors[data.phrase]); delete spellerrors[data.phrase]; >break; case ‘spellcheck: success’: w.sum.add(data.spellerrors); spellerrors[data.phrase] = data.spellerrors; break;
Итого
В сумме по всем файлам: 200 строк HTML+JavaScript, 5.5KB. Из них почти 50 строк — документация/комментарии.
За кадром остались вопросы обработки ошибок, тестирования, логирования и отладочного режима. Тут я ничего нового добавить не могу, всё стандартно (например смотрите доклад Nicholas Zakas «Enterprise JavaScript Error Handling»: слайды).
Дополнительная информация
Исходники описываемого в статье приложения выложены на bitbucket, с пошаговыми commit-ами соответствующими статье. Так же можно посмотреть на само приложение.
Желающие покритиковать мой подход к реализации модулей могут для начала ознакомиться с моим мнением о других подходах. Плюс когда-то была небольшая дискуссия о моём js-шаблонизаторе.
Источник: habr.com
Интересные проекты на JavaScript для разработчиков всех уровней
JavaScript временами кажется запутанным и откровенно странным, но с его помощью можно сделать много замечательных вещей.
Разработчики, изучающие JS, обычно ограничиваются решением задачек по программированию и классическими веб-проектами вроде сайта-портфолио или онлайн-магазина. Но зачем же на этом останавливаться?
Забудьте на минутку о скучных упражнениях и включите свое воображение. На JavaScript можно сделать очень много всего. Давайте немного развлечемся!
101 Hello Word
Программы типа «Hello Word» годятся не только для начинающих. Проверьте, насколько вы креативны! Попробуйте придумать несколько разных способов написать на JavaScript программу, которая выводила бы на экран эти всем известные слова. Если не найдете свое решение в этом GitHub-репозитории, не стесняйтесь поделиться им с другими!
Создание 3D-объектов
Чтобы взяться за 3D-рендеринг в JavaScript, не так уж много надо. Достаточно иметь базовое понимание языка и познакомиться с библиотекой Three.js.
Все новички начинают с рисования 3D-куба или сферы, но, освоив азы, вы сможете создавать по-настоящему потрясающие вещи.
Библиотека Three.js также часто используется в VR и AR-проектах, так что кто знает, куда вас заведет такой проект.
Космические захватчики (Space Invaders)
YouTube-канал Coding Train — настоящий монстр в том, что касается необычных JavaScript-челленджей и проектов. Если вы уже имеете кое-какой опыт, я бы посоветовала попробовать самостоятельно придумать свою версию игры «Space Invaders» и затем сравнить результаты.
А новички могут просто следовать этому замечательному туториалу. Создавая игру, они узнают много новых интересных вещей о JavaScript и основах геймдева.
Вот общий обзор фракталов и фрактальных деревьев — чтобы было, от чего отталкиваться. А если вам нужна дополнительная помощь, обратитесь к этому руководству.
Множество Мандельброта
Раз уж зашла речь о фракталах, вот мой второй фаворит — множество Мандельброта. Если вам вдруг любопытно, третий фаворит — множество Жюлиа.
Попробуйте сперва поискать информацию о фракталах и самостоятельно реализовать собственную версию. Если что-то не сработает, вот набор полезных руководств.
Игра «Жизнь» Конвея
В 1970-х математик Джон Конвей придумал игру «Жизнь». Это не то чтобы прямо настоящая игра. Это скорее захватывающая математическая симуляция, которую довольно легко реализовать на любом языке программирования, в том числе на JavaScript.
Симулятор брутфорс-хакинга
Это скорее задача по фронтенду, но здесь есть своя доля интересных проблем, которые нужно решить.
Куайн
Куайн (Quine) — программа, выдающая на выходе точную копию своего исходного текста. Написание такой программы может поначалу показаться простой задачей, но при желании вы можете ее усложнить. Попробуйте сперва придумать собственное решение, не заглядывая в чужие!
Имитация природных явлений
На Khan Academy в бесплатном курсе по программированию есть целый юнит, посвященный продвинутому JS и имитациям природы. Посмотрите сперва их видео, а затем выполните задания. Если вы новичок, вам может понадобиться посмотреть и несколько предыдущих юнитов, а то и весь курс.
Музыка и Tone.js
Проект, в котором сочетается создание музыки и программирование на JavaScript, может быть очень интересным. Создание музыки вообще имеет больше общего с написанием кода, чем можно подумать.
При работе над таким проектом вам пригодится библиотека Tone.js. Она довольно понятная и работать с ней просто.
AI с TensorFlow.js
Когда думаешь о машинном обучении или искусственном интеллекте, JavaScript — не первый язык, который приходит в голову. Тем не менее, с помощью библиотеки TensorFlow.js можно создать что-то по-настоящему крутое.
Симуляция физики
Замечательное руководство по созданию веб-симуляций с использованием HTML5 и JavaScript. Доступно в PDF и веб-версии.
Вирус
И, наконец, последняя задача — создание вируса на JavaScript. Конечно, это должно быть не что-то страшное, а чистое озорство. Но даже нестрашный вирус будет интересным проектом. Пример на GitHub.
Источник: techrocks.ru
Советы по разработке больших приложений JavaScript
Длительность одного клиентского проекта в нашем агентстве 9elements обычно составляет пару месяцев. Процесс начинается с первого контакта с клиентом, проходит этап проектирования и заканчивается реализацией и запуском проекта, что ориентировочно занимает полгода. Но бывают и такие случаи, когда разработка и обслуживание программного обеспечения продолжается в течение нескольких лет.
К примеру, запуск GED VIZ для фонда Bertelsmann произошел в 2012 году, выпуск — в 2013 году, а новые функции и данные добавлялись каждые несколько лет. В 2016 году мы провели значительную оптимизацию базовой визуализации, превратив ее в библиотеку многократного использования. Механизм визуализации потока данных до сих пор используется Европейским Центральным банком (ЕЦБ). Еще одним долгосрочным проектом является разработка внешнего портала данных ОЭСР: Реализация началась в 2014 году, а расширение кодовой базы продолжается до сих пор.
После завершения основной фазы разработки мы исправляем ошибки и добавляем новые функции. Как правило, для крупного рефакторинга или даже рерайта не существует определенного бюджета. По этой причине, в некоторых проектах у меня возникали трудности с кодом, написанным 4–6 лет назад, и библиотечным стеком, бывшим в то время в моде.
Замена крупномасштабного рерайта небольшими улучшениями
Оба вышеупомянутых проекта являются крупными клиентскими приложениями JavaScript. Сейчас очень трудно найти информацию по поводу обновления существующей кодовой базы JavaScript. Тем не менее, поиск статьи на тему рерайта фронтенда с любой популярного на данный момент JavaScript-фреймворка не составит труда.
Переход на новый набор библиотек и инструментов требует значительных инвестиций, однако они могут окупиться в ближайшее время. Упрощается обслуживание. Снижается стоимость изменений. Ускоряется процесс выполнения итерации и реализации новых функций. Количество ошибок уменьшается, улучшается устойчивость и производительность.
В конечном итоге такие инвестиции могут снизить общую стоимость собственности.
Однако, в случае если клиент не может инвестировать в этот проект, необходимо искать пути постепенного улучшения существующей кодовой базы.
Обучение на основе долгосрочных проектов
Некоторые веб-разработчики до ужаса боятся столкновения с существующей кодовой базой. Код, не написанный ими в ближайшее время, они называют устаревшим.
Однако, все совсем наоборот. Поддержка одного кода в проекте в течение нескольких лет может дать больше знаний о разработке программного обеспечения, чем несколько недолговечных, резко выстреливающих, но так же быстро угасающих проектов.
Прежде всего, я столкнулся с кодом, написанным мною много лет назад. Решения, принятые много лет назад, до сих пор оказывают влияние на всю систему. Решения, сделанные сегодня, определяют судьбу системы в долгосрочной перспективе.
Главный вопрос: какие изменения можно внести сейчас? Что необходимо улучшить? У каждого разработчика порой возникает желание уничтожить все, что есть, и начать с нуля.
Но в большинстве случаев, очень трудно найти проблемы с существующим кодом: кажется, что логику написания стоит сохранить, однако необходима другая структура. Здесь представлены основные проблемы, связанные со структурой кода JavaScript.
Избегайте сложных структур
“Сложные” — не означает большие. В каждом нетривиальном проекте содержится много логики. Много случаев, требующих рассмотрения и проверки. Множество различных данных, требующих обработки.
Сложности возникают в результате переплетения различных проблем. Этого нельзя избежать полностью, однако, если разобрать эти проблемы по отдельности, то в результате их можно собрать воедино определенным контролируемым способом.
Рассмотрим простые и сложные структуры данных в JavaScript.
Функции
Простейший повторно используемый фрагмент кода JavaScript — это функция. В частности, чистая функция — это та, которая получает информацию и выдает результат (возвращаемое значение). Функция непосредственно получает все необходимые данные в качестве параметров. Входные данные или контекст данных не изменяются. Такую функцию легко писать, легко тестировать, легко фиксировать и легко применять.
Для написания хорошего кода на JavaScript не обязательно наличие высокоуровневых шаблонов проектирования. В первую очередь, необходимо умение использовать самую простую технику разумным и целесообразным способом: программу следует структурировать с помощью функций, правильно выполняющих одно определенное действие. Затем низкоуровневые функции необходимо сопоставить с высокоуровневыми.
Функции в JavaScript представляют собой полноценные значения, также называемые объектами первого класса. Как мультипарадигмальный язык программирования, JavaScript допускает использование мощных функциональных шаблонов программирования. Даже обычное понимание основ уже помогает писать более простые программы.
Объекты
Объект представляет собой еще одну сложную структуру. Простейший объект выполняет сопоставление строк произвольным значениям, лишенным логики. Однако и он может содержать логику: Функции становятся методами при присоединении к объекту.
const cat = < name: ‘Maru’, meow() < window.alert(`$says MEOW`); >>;cat.meow();
Объекты в JavaScript достаточно распространены и являются универсальными. Объект может использоваться как пакет параметров с несколькими обработчиками функций. Помимо группировки связанных значений, объект может структурировать программу. Например, можно поместить несколько похожих функций на один объект и позволить им работать с одними и теми же данными.
Классы
Самая сложная структура в JavaScript — это класс. Он представляет собой основу для объектов и, в то же время, производит эти самые объекты. Он представляет собой смесь прототипного наследования с созданием объектов. Он переплетает логику (функции) с данными (свойствами экземпляра). Иногда в функции конструктора содержатся свойства, называемые “статическими” свойствами.
Такие шаблоны, как “singleton”, перегружают класс еще большим количеством логики.
Классы достаточно часто применяются в объектно-ориентированных языках, однако они требуют знания шаблонов проектирования и опыта в объектном моделировании. В JavaScript они требуют особого способа управления: Построение цепочек наследования, композиция объектов, применение миксинов, методов вызова, работа со свойствами экземпляра, геттерами и сеттерами, привязка методов, инкапсуляция и т.д. Поскольку ECMAScript не предоставляет стандартных решений для общих концепций ООП, сообщество не согласовало лучшие методы использования классов.
Использование классов допустимо, если они имеют одну определенную цель. Согласно моему опыту, следует избегать добавления большого количества вопросов в класс. К примеру, компоненты в React, содержащие внутреннее состояние, обычно объявляются как классы. Это имеет смысл лишь для конкретной проблемной области. Они обладают одной определенной целью: Группировка данных props и state и пары функций, работающих с этими типами данных. В центре класса находится функция render .
Не стоит переполнять эти классы большим количеством слабо связанной логики. Следует отметить, что команда React постепенно переходит от классов к функциональным компонентам, содержащим внутреннее состояние.
Выбор структур
Несколько рекомендаций согласно моему многолетнему опыту:
- Используйте самую простую, самую гибкую и самую универсальную структуру: функцию. При возможности лучше использовать чистую функцию.
- По возможности избегайте смешивания данных и логики в объекте.
- По возможности избегайте использования классов. В случае их использования, они должны выполнять одно определенное действие.
Большинство фреймворков JavaScript имеют свой собственный способ структурирования кода. В основе компонентно-ориентированных UI фреймворков, таких как React и Angular, компоненты обычно представлены объектами или классами. Лучше отдать предпочтение композиции, а не наследованию: Чтобы разделить задачу на несколько частей, просто создайте новый облегченный класс компонентов.
Это не означает, что для моделирования бизнес-логики необходимо придерживаться только этих структур. Лучше поместить эту логику в функции и отделить их от структуры пользовательского интерфейса. Благодаря этому можно изменять код фреймворка и бизнес-логику по отдельности.
Большое количество модулей
Раньше управление зависимостями между файлами JavaScript и внешними библиотеками доставляло множество неприятностей. Первое время мы применяли модули CommonJS или AMD. Позже сообщество остановилось на стандартных модулях ECMAScript 6.
Модули стали важной структурой кода в JavaScript. В зависимости от использования, модули могут упрощать или усложнять структуру.
Со временем я стал использовать модули совершенно по-другому. Раньше я создавал довольно большие файлы с несколькими экспортами. В противном случае, один экспорт представлял собой огромный объект группировки множества констант и функций. Сейчас я стремлюсь к созданию небольших плоских модулей с одним или несколькими экспортами. В результате, один файл приходится на одну функцию, один файл на один класс и так далее. Файл foo.js будет выглядеть так:
export default function foo(…)
Или так, в случае указания имени экспорта:
export function foo(…)
В результате упрощается обращение к отдельным функциям и их повторное использование. Согласно моему опыту, множество маленьких файлов не обладают значительной стоимостью. Они позволяют легче ориентироваться в коде. Кроме того, зависимости определенного фрагмента кода объявляются более эффективно.
Избегайте создания нетипизированных объектов
Одной из лучших особенностей JavaScript является объектный литерал. Благодаря нему ускоряется создание объектов с произвольными свойствами.
const cat = < name: ‘Maru’, meow() < window.alert(`$says MEOW`); >>;
Формат JSON настолько прост и выразителен, что со временем он превратился в независимый формат данных, повсеместно распространенный в наши дни. Однако с каждой новой версией ECMAScript, объектный литерал приобретал все больше и больше возможностей, превышающих его первоначальное назначение. Благодаря новым особенностям ECMAScript, таким как Object Rest/Spread, создавать и смешивать объекты стало стало намного проще.
В небольшой кодовой базе создание объектов “на лету” является функцией повышения производительности. Однако в большой кодовой базе объектные литералы становятся помехой. На мой взгляд, в подобных проектах не следует допускать использование объектов с произвольными свойствами.
Проблема не в самом литерале объекта. Проблема заключается в объектах, которые не соответствуют центральному определению типа. Часто они становятся причиной ошибок во время выполнения : Свойства могут существовать или не существовать, могут обладать определенным типом или не обладать им вовсе. Помимо всех необходимых свойств, объект так же может иметь и другие. Читая код, невозможно определить, какие свойства будут у объекта во время выполнения.
В JavaScript нет определений типов, но есть несколько способов создания объектов более упорядоченным способом. Например, функция может использоваться для создания похожих объектов. Функция гарантирует наличие необходимых свойств, а также допустимость или наличие значения по умолчанию. Также можно использовать класс, создающий простейший шаблон объект-значение.
Аналогичным образом функция может выполнить проверку использования аргумента во время выполнения. Она может непосредственно выполнить проверку типа с использованием typeof , instanceof , Number.isNaN и т. д. или неявно, с использованием утиной типизации.
Дополнение JavaScript определениями типов, такими как TypeScript или Flow, представляет собой более всесторонний подход. К примеру, работа с TypeScript начинается с определения интерфейсов для важных моделей данных. Функции объявляют тип своих параметров и возвращаемых значений. Компилятор TypeScript дает гарантию передачи только разрешенного типа — при условии доступа компилятора ко всем вызовам.
Надежный код
Эти рекомендации касаются общей структуры кода. Есть еще много техник и методов, которые я изучил за годы работы над большими проектами JavaScript. Самое большое влияние на надежность приложений JavaScript оказывает знание факторов, способных приостановить работу программы JavaScript и умение предотвратить их появление.
Источник: nuancesprog.ru