Одной из первых проблем, с которыми сталкиваются разработчики, впервые знакомые с JavaScript и создающие большие приложения, является вопрос организации своего кода. Большинство начинается с встраивания сотен строк кода между тегом <script>, который работает, но быстро превращается в беспорядок. Трудно то, что JavaScript не предлагает очевидной помощи в организации нашего кода. Буквально там, где C # использует, в Java есть импорт — в JavaScript нет ничего. Это заставило авторов JavaScript экспериментировать с различными соглашениями и использовать язык, который нам нужен, для создания практических способов организации больших приложений JavaScript.
Шаблоны, инструменты и практики, которые станут основой современного JavaScript, должны исходить от внешних реализаций самого языка.
Шаблон модуля
Один из наиболее широко используемых подходов для решения этой проблемы известен как шаблон модуля. Я попытался объяснить базовый пример ниже и поговорить о некоторых его свойствах. Для лучшего описания и фантастического разнообразия различных подходов взгляните на пост Бена Черри — Шаблон модуля JavaScript: Углубленный анализ .
(function(lab49) { function privateAdder(n1, n2) { return n1 + n2; } lab49.add = function(n1, n2) { return privateAdder(n1); }; })(window.lab49 = window.lab49 || {});
В приведенном выше примере мы использовали ряд базовых возможностей языка для создания конструкций, подобных тем, которые мы видим в таких языках, как C # и Java.
Изоляция
Вы заметите, что код обернут внутри функции, которая вызывается немедленно (проверьте последнюю строку). По умолчанию в браузере файлы JavaScript оцениваются в глобальной области видимости, поэтому все, что мы объявили внутри нашего файла, будет доступно везде. Представьте, что если в lib1.js у нас был оператор var name = ‘…’, то в lib2.js у нас был другой оператор var name = ‘…’. Второе выражение var заменит значение первого — не хорошо. Однако, поскольку JavaScript имеет область видимости функции, в приведенном выше примере все объявляется в своей области видимости вне глобальной. Это означает, что все в этой функции будет изолировано от всего, что происходит в системе.
Пространства имен
В последней строке вы заметите, что мы присваиваем window.lab49 самому себе или литералу пустого объекта. Это выглядит немного странно, но давайте пройдемся по воображаемой системе, где у нас есть несколько js-файлов, все с использованием вышеупомянутой функции-оболочки.
Первый включенный файл оценит этот оператор OR и обнаружит, что левая часть не определена. Это ложное значение, поэтому оператор OR пойдет дальше и оценит правую часть, в данном случае пустой литерал объекта. Оператор OR на самом деле является выражением, которое будет возвращать свой результат и идти вперед и назначить его глобальному window.lab49.
Теперь следующий файл, который будет использовать этот шаблон, перейдет к оператору OR и обнаружит, что window.lab49 теперь является экземпляром объекта — истинное значение. Оператор OR закорачивает и возвращает это значение, которое сразу же присваивается ему — фактически ничего не делая.
Результатом этого является то, что первый файл в будет создавать наше пространство имен lab49 (просто объект JavaScript), и каждый последующий файл, использующий эту конструкцию, будет просто повторно использовать существующий экземпляр.
Частное государство
Как мы только что говорили из-за нахождения внутри функции, все, что объявлено внутри нее, находится в области действия этой функции, а не в глобальной области. Это прекрасно, чтобы изолировать наш код, но он также имеет эффект, что никто не может его вызвать. Довольно бесполезно.
Как мы только что говорили, мы создаем объект window.lab49 для эффективного пространства имен нашего контента. Эта переменная lab49 доступна глобально, так как она прикреплена к объекту окна. Вы можете сказать, что для того, чтобы выставить вещи за пределы нашего модуля, все, что нам нужно сделать, это прикрепить значения к этой глобальной переменной. Так же, как мы делаем с нашей функцией добавления в примере выше. Теперь вне нашего модуля наша функция add может быть вызвана с помощью lab49.add (2, 2).
В качестве еще одного результата объявления наших значений внутри этой функции, если значение явно не отображается путем присоединения его к нашему глобальному пространству имен или к чему-то за пределами модуля, внешний код не может достичь его. На практике мы только что создали некоторые частные ценности.
CommonJS Модули
CommonJS — это группа, в основном состоящая из авторов сред выполнения JavaScript на стороне сервера, которые пытались стандартизировать доступ к модулям и доступ к ним. Однако стоит отметить, что предложенная ими модульная система не является стандартом из той же группы, которая создает стандарт JavaScript, поэтому она стала скорее неформальным соглашением между авторами сред выполнения JavaScript на стороне сервера.
Я обычно поддерживаю идею CommonJS, но давайте проясним: вряд ли это спецификация, переданная богами (как ES5); просто некоторые люди обсуждают идеи в списке рассылки. Большинство из этих идей не имеют реальных реализаций.
— Райан Даль , создатель node.js
Суть спецификации модулей относительно проста. Модули оцениваются в своем собственном контексте и имеют доступ к глобальной переменной экспорта. Эта переменная экспорта — это просто старый объект JavaScript, который вы тоже можете прикрепить, аналогично объекту пространства имен, который мы продемонстрировали выше. Для доступа к модулю вы вызываете глобальную функцию require и даете идентификатор запрашиваемого пакета. Затем он оценивает модуль и возвращает все, что было прикреплено к экспорту. Этот модуль будет затем кэшироваться для последующих вызовов по требованию.
// calculator.js exports.add = function(n1, n2) { }; // app.js var calculator = require('./calculator'); calculator.add(2, 2);
Если вы когда-нибудь играли с Node.js, вы наверняка найдете знакомое выше. Способ, которым Node реализует модули CommonJS, на удивление прост, если посмотреть на модуль внутри инспектора узлов (отладчик Node), то покажет его содержимое, заключенное в функцию, которой передаются значения для экспорта и require. Очень похоже на раскрученные вручную модули, которые мы показали выше.
Есть несколько проектов узлов ( Stitch и Browserify ), которые выводят модули CommonJS в браузер. Серверный компонент объединит эти отдельные js-файлы модуля в один js-файл с созданной оболочкой модуля.
CommonJS был в основном разработан для серверной среды выполнения JavaScript, и поэтому есть несколько свойств, которые могут затруднить организацию клиентского кода в браузере.
- require должен немедленно вернуться — это прекрасно работает, когда у вас уже есть весь контент, но затрудняет использование загрузчика скриптов для асинхронной загрузки скрипта.
- Один модуль на файл — чтобы объединить модули CommonJS, их нужно обернуть в функцию, а затем каким-то образом организовать. Это затрудняет их использование без какого-либо серверного компонента, подобного упомянутому выше, и во многих средах (ASP.NET, Java) их еще нет.
Определение асинхронного модуля
Определение асинхронного модуля (обычно известное как AMD) было разработано как формат модуля, подходящий для браузера. Он начал свою жизнь как предложение группы CommonJS, но с тех пор перешел на
GitHub и теперь сопровождается
набором тестов для проверки соответствия AMD API для авторов модульной системы.
Ядром AMD является определяющая функция. Наиболее распространенный способ вызова define принимает три параметра — имя модуля (это означает, что он больше не связан с именем файла), массив идентификаторов модулей, от которых зависит этот модуль, и фабричная функция, которая будет возвращать определение модуля. (Существуют и другие способы вызова определения — обратитесь к
AMD Wiki за полной информацией).
define('calculator', ['adder'], function(adder) { return { add: function(n1, n2) { return adder.add(n1, n2); } }; });
Поскольку это определение модуля заключено в вызове define, это означает, что вы можете счастливо иметь несколько модулей в одном файле js. Кроме того, поскольку загрузчик модулей контролирует, когда вызывается функция определения фабрики модулей, он может разрешать зависимости в свое время, что удобно, если эти модули необходимо сначала загрузить асинхронно.
Были приложены значительные усилия, чтобы сохранить совместимость с первоначальным предложением модуля CommonJS. Существует специальное поведение для использования require и export в функции фабрики модулей, что означает, что традиционные модули CommonJS могут быть вставлены прямо в.
AMD выглядит очень популярным способом организации клиентских JavaScript-приложений. Будь то через загрузчики ресурсов модуля, такие как RequireJS или curl.jsили JavaScript-приложения, которые недавно приняли AMD, такие как Dojo .
Значит ли это, что JavaScript — отстой?
Отсутствие каких-либо языковых конструкций для организации кода в модули может быть довольно неприятным для разработчиков из других языков. Однако, поскольку этот недостаток вынудил разработчиков JavaScript придумывать свои собственные шаблоны для того, как были структурированы модули, мы смогли повторить и улучшить по мере развития приложений JavaScript. Следуйте за блогом Tagneto для некоторого понимания этого.
Представьте, был ли этот тип функциональности включен в язык 10 лет назад. Маловероятно, что они представили бы требования для запуска больших приложений JavaScript на сервере, асинхронной загрузки ресурсов в браузере или включения таких ресурсов, как текстовые шаблоны , которые могут выполнять такие загрузчики, как RequireJS.
Модули рассматриваются как функция уровня языка для Harmony / ECMAScript 6 . Благодаря мысли и кропотливой работе авторов модульных систем за последние несколько лет, гораздо более вероятно, что то, что мы в итоге получим, подойдет для того, как создаются современные приложения JavaScript.
Источник: http://blog.davidpadbury.com/2011/08/21/javascript-modules/