JavaScript – это очень любопытный язык. На нем очень легко писать код, но достаточно сложно его изучить.
Почему JavaScript`ом трудно овладеть?
При написании JS кода важно помнить что, прежде всего, это динамический язык. Это означает, что существует много способов сделать разные вещи. Вам не придется иметь дело с сильно типизированными классами, или с более сложными функциями языков, таких как C # и Java. Это как благословение и проклятие одновременно.
Усовершенствование JS
Namespaces
Один из недостатков того, как JavaScript реализуется, это то, что он работает на вершине глобального объекта. В случае браузера, это означает window объект. Поэтому, в любое время, когда присутствует такой код на странице…
function doStuff(){ alert('Привет! Я работаю!'); } function doMoreStuff(){ var images = document.images.length; console.log("Имееется " + images + "на этой странице"); } doStuff(); doMoreStuff();
Функции doStuff и doMoreStuff сразу доступны глобальному window объекту.
Это означает, что если кто-то прийдет и попытается написать функцию, которая также называется doStuff, то тогда произойдет конфликт! Все script теги, в основном берут код внутри себя, и работают против window, в том порядке, в котором на них ссылаются в HTML. В результате, второй человек при реализации doStuff, перепишет первый doStuff.
Распространенный метод устранения этой проблемы, это использование либо самоисполнительной анонимной функции или namespace. Объектно-ориентированные люди, читающие это, вероятно, уже знакомы с концепцией namespace, но основная идея состоит в том, чтобы сгруппировать функции в различных областях для повторного использования.
var NS = NS || {}; // "Если NS не определен, тогда сделайте его равным пустому объекту" NS.Utils = NS.Utils || {}; NS.Models = NS.Models || {}; NS.Views = NS.Views || {};
Это позволит предотвратить загрязнение глобального namespace, и будет способствовать читаемости вашего приложения. Теперь, вы можете просто определить функции в своих namespace. Общепринятым namespace, является app, которое управляет остальными частями приложения.
Шаблоны проектирования и практика
В каждом из языков, существует множество шаблонов проектирования. Часто используемым паттерном является Revealing Module.
NS.App = (function () { // Инициализация приложения var init = function () { NS.Utils.log('Application initialized...'); }; // Верните общедоступный метод для App return { init: init }; }()); NS.App.init();
Выше, функция App определяется в NS объекте. Внутри, определяется функциональная переменная для init, и возвращена как anonymous object literal. Обратите внимание, что в конце, есть дополнительный набор скобок: }());. Это вынуждает NS.App функцию автоматически выполняться и возвращаться. Теперь, вы можете вызвать NS.App.init () для инициализации вашего приложения.
Анонимная функция выше, является наилучшей практикой в JavaScript, и еще её называют Self-Executing Anonymous Function (само-исполнительная анонимная функция). Поскольку у каждой функци в JavaScript есть своя собственная сфера – то есть переменные, определенные внутри функции не будут доступны вне их, это делает анонимные функции, полезными во многих вещах.
// Оберните код в SEAF (function (global) { // Теперь любые переменные, которые вы объявить здесь,не будут доступны снаружи. var somethingPrivate = 'you cant get to me!'; global.somethingPublic = 'but you can however get to me!'; }(window)); console.log(window.somethingPublic); // This works... console.log(somethingPrivate); // Error
В этом примере, так как функция автоматически выполняется, вы можете передать window на выполнение }(window));, и он будет представлен в качестве global внутри анонимной функции. Эта практика ограничивает глобальные переменные в window объекте, и оказывать помощь в предотвращении конфликтов имен.
Теперь вы можете начать использовать SEAF в других областях вашего приложения, чтобы сделать код более модульным. Это позволит избежать повторного использования вашего кода, а также поспособствует хорошему разделению интересов.
Вот пример потенциального использования этих идей:
(function ($) { var welcomeMessage = 'Welcome to this application!' NS.Views.WelcomeScreen = function () { this.welcome = $('#welcome'); }; NS.Views.WelcomeScreen.prototype = { showWelcome: function () { this.welcome.html(welcomeMessage) .show(); } }; }(jQuery)); $(function () { NS.App.init(); }); // Измените App.init выше var init = function () { NS.Utils.log('Application initialized...'); this.welcome = new NS.Views.WelcomeScreen(); this.welcome.showWelcome(); };
Итак, здесь происходит несколько разных вещей. Во-первых, jQuery передается в качестве аргумента в анонимную функцию. Это гарантирует, что $ на самом деле является jQuery внутри анонимной функции.
DOM запросы, могут занимать большой объем памяти, поэтому, пожалуйста, кэшируйте их как можно больше.
Затем, мы обворачиваем (wrap) App init внутри $(function(){});, что было бы эквивалентно $(document).ready().
Шаблон Наблюдатель (Observer Pattern)
Еще один отличный шаблон – Наблюдатель. Этот паттерн позволяет нам подписаться на DOM события, такие как click и mouseover. С одной стороны, мы слушаем эти события, и с другой стороны, публикуем эти события, например, когда браузер публикует (или объявляет), что кто-то нажал на конкретный элемент. Существует большое количество библиотек для шаблона наблюдателя, так как это достаточно короткий фрагмент кода.
// Модель данных для получения новостей. NS.Models.News = (function () { var newsUrl = '/news/' // Получить новости var getNews = function () { $.ajax({ url: newsUrl type: 'get', success: newsRetrieved }); }; var newsRetrieved = function (news) { // Публикация поиска новостей amplify.publish('news-retrieved', news); }; return { getNews: getNews }; }());
Этот код определяет модель для извлечения новостей из какого-то сервиса. Как только новость была получена с помощью AJAX, срабатывает newsRetrieved метод, проходя через извлеченные новости для Amplify, и публикуется на news-retrieved тему.
(function () { // Создайте просмотры новостей. NS.Views.News = function () { this.news = $('#news'); // Подпишитесь на событие поиска новостей. amplify.subscribe('news-retrieved', $.proxy(this.showNews)); }; // Показать новости, когда они прибыли NS.Views.News.prototype.showNews = function (news) { var self = this; $.each(news, function (article) { self.append(article); }); }; }());
Этот код является способом для отображения полученых новостей. В конструкторе News, Amplify подписывается на news-retrieved тему. Когда эта тема будет опубликована, сработает showNews функция. Затем новость добавляется в DOM.
// Модификация App.init выше var init = function () { NS.Utils.log('Application initialized...'); this.welcome = new NS.Views.WelcomeScreen(); this.welcome.showWelcome(); this.news = new NS.Views.News(); // Получи новости! NS.Models.News.getNews(); };
Изменените init функцию из приложения, чтобы добавить поиск новостей … и все готово! Теперь, у вас есть отдельные части приложения, каждый из которых отвечает за одну операцию. Это также известно, как Принцип Единой Ответственности (Single Responsibility Principle).
Документация и Файлы + Минификация
Один из ключей к поддерживаемому коду любого рода – не только JS – это документация и комментарии. Комментарии могут стать драгоценными для новых разработчиков приступающих к проекту, с необходимостью понять, что происходит в коде. Отличный инструмент для создания документации называется, Docco. Это тот же самый инструмент, который генерирует документацию для веб-сайта Backbone.js. В основном, он берет ваши комментарии, и помещает их рядом с вашим кодом.
Есть также инструменты, такие как JSDoc, которые генерируют документацию API стилей, описывая каждый класс в коде.
Другое дело, которое может оказаться трудным, при запуске нового проекта, это попытки определить, как лучше организовать код. Один из способов заключается в разделении функциональных элементов в отдельные папки. Например:
- /app.js
- /libs/jquery.js
- /libs/jquery-ui.js
- /users/user.js
- /views/home.js
Эта структура помогает сохранить части функциональности, отдельно друг от друга. Есть, конечно, несколько способов организации кода, но все, что действительно имеет значение, это принятие решения о структуре. Далее, вы можете использовать инструменты сборки и минимизации. Есть много вариантов:
- Grunt
- Google Closure
- JSMin
- YUI Compressor
Эти средства помогут вырезать пробелы, удалят комментарии, и объединят все указанные файлы в один. Это уменьшает размер файлов и HTTP-запросы для приложения. Это означает, что вы можете хранить все ваши файлы отдельно в процессе разработки, и в сочетании в производстве.
AMD
Асинхронный Определение Модуля (Asynchronous Module Definition) является иннымспособ написания JavaScript кода, он делит весь код в отдельные модули. AMD создает стандартный шаблон для написания этих модулей, чтобы загрузить их в код асинхронно.
Использование script тегов блокирует страницы, так как они загружается до тех пор пока DOM не будет готов. Поэтому, используя что-то вроде AMD, позволит DOM продолжать загрузку, в то время как скрипты также еще грузятся. По существу, каждый модуль делится на свой собственный файл, и также есть один файл, который стартует процесс. Самая популярная реализация AMD является RequireJS.
// main.js require(['libs/jquery','app.js'], function ($, app) { $(function () { app.init(); }); }); // app.js define(['libs/jquery', 'views/home'], function ($, home) { home.showWelcome(); }); // home.js define(['libs/jquery'], function ($) { var home = function () { this.home = $('#home'); }; home.prototype.showWelcome = function () { this.home.html('Welcome!'); }; return new home(); });
В сниппете выше, есть main.js файл, в котором начинается весь процесс . Первый аргумент к требованию функции представляет собой массив из зависимостей. Эти зависимости являются списком файлов, которые необходимы для app.js. По окончанию загрузки, вернувшийся модуль передается в качестве аргумента функции обратного вызова справа.
Кроме того, есть app.js, который требует jQuery, а также view. Следующее, view home.js требует только jQuery. У него есть home функция внутри, и он возвращает экземпляры самого себя. В приложении эти все модули хранятся в отдельных файлах, что делает приложение очень легким в управлении.
Высоких конверсий!