Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Модули

Видео может быть заблокировано из-за расширений браузера. В статье вы найдете решение этой проблемы.

Что такое модуль и зачем он нужен

Модули в JavaScript (ES6) реализованы на основе файлов, и находятся они с файлами в отношении "один-к-одному". Попросту говоря, какой-либо конкретный файл, содержащий js-код, можно смело считать отдельным модулем. При этом, как следует из вышенаписанного, один файл представляет лишь один модуль, в нём не может быть (по крайней мере, на текущий момент) организовано размещение одновременно нескольких модулей.

Любой ваш проект (программа, задача, приложение) - это код. И этого кода может быть очень много. Но даже в небольших по объёму проектах без должной структуризации кода весьма легко запутаться в используемых константах и переменных (далее - переменные), функциях и классах и допустить ошибки. Так вот, базовым и наиболее естественным средством структуризации вашего кода служит его разбиение на отдельные модули. И это помогает решить ряд задач и проблем:

  • С использованием модулей ваш код становится легче писать, сопровождать и читать - в общем, его качество растёт по "всем фронтам". Конечно, для этого надо грамотно классифицировать код и осуществить его разбиение по модулям, но это тема, даже не статей, а целых книг. Тем не менее, даже построение на самом базовом уровне системы модулей, каждый из которых несёт чётко определённую функциональность (имеет свою зону ответственности) даёт по сравнению с неструктурированным кодом все те профиты, о которых писалось выше. Обратите внимание, как устроена каждая практика в наших курсах: в каждом упражнении есть модуль (один или несколько), в котором нужно написать программу (функцию); отдельный модуль выделен для тестирования вашего кода; возможны дополнительные модули, в которых содержатся вспомогательные функции.

    responsibility

  • Разрешаются конфликты имён. А они неизбежно возникают, когда в коде используются от нескольких десятков до нескольких сотен (и более!) идентификаторов и все они находятся в одной общей области видимости. Теперь же, разделив код на модули, в каждом из которых свои собственные области видимости, никак не пересекающиеся между собой, Вам не надо "держать и перебирать" в голове весь остальной код, не относящийся к модулю, с которым вы в настоящий момент работаете - очевидно, задача разрешения (предотвращения) конфликта имён кардинально упрощается.

    collision

  • Меньше ошибок, выше надёжность. С помощью модулей осуществляется инкапсуляция кода. Модуль изолирует одну часть вашего проекта от других, одновременно предоставляя возможность контролируемого доступа к отдельным своим компонентам - "всему остальному миру" (другим модулям) будут видны только те переменные, функции и классы, которые вы сами посчитаете нужным экспортировать (инструкции export и/или default export). Таким образом снижается вероятность непреднамеренного или злонамеренного изменения кода одной части программы из другой части, повышается уровень безопасности.

    capsule

Далее будут подробно рассмотрены синтаксис модулей, способы и примеры экспортирования и импортирования.

Анализ кода

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

При работе с модулями будет полезным сразу наработать некоторые модели поведения, позволяющие вам с легкостью определять, какой код вам доступен на исполнение, откуда пришел этот код и как его увидеть. Фидбек, который мы получаем от наших пользователей, говорит о том, что, как раз, в этом моменте больше всего затыков.

Ниже описан основной алгоритм, по которому нужно анализировать файл с кодом, над которым вы сейчас работаете. Этот алгоритм не является специфичным для работы в среде Хекслета, так нужно делать в принципе:

  1. Внимательно изучите все импорты, описанные в начале файла. Так вы узнаете, какие модули и функции доступны внутри вашего файла (не считая глобальных функций и модулей, которые доступны и без импорта, например, Math).
  2. Попробуйте классифицировать импортируемые функции. Если импорт выглядит так from './..., то есть содержит ./, значит импортируется модуль, содержимое которого находится в текущей файловой системе. Это автоматически означает несколько вещей. Первое, вы всегда можете открыть этот файл и посмотреть, что там написано, второе, вы не сможете импортировать этот модуль в другой среде, что очевидно, ведь этого файла там нет.
  3. Если from 'name' содержит только имя, без лидирующего ./, это означает, что модуль подгружается либо из стандартной библиотеки nodejs, либо из установленных пакетов. Визуально невозможно отличить одно от другого. В данном случае необходимо проявить смекалку и попробовать загуглить имя таким способом nodejs name. Если в выдаче будет ссылка на официальную документацию, то значит это модуль ноды, если на репозиторий npm, значит это обычный пакет, который почти наверняка лежит на гитхабе, что можно проверить таким запросом github js name, где name это имя пакета.

Дополнительные возможности

Варианты импорта

Если из модуля экспортировано множество функций, то, как мы уже знаем, импортировать их можно с помощью такой строчки:

import * as name from './name';
// name.someFunction();

Иногда бывает удобнее импортировать функции в текущий модуль напрямую, без необходимости указывать исходный модуль, и достичь этого можно таким образом:

import { someFunction, anotherFunction } from './name';
// someFunction();

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

import { request as r, get } from 'http';

r();

Варианты экспорта

По умолчанию также можно экспортировать любую константу, что открывает такую возможность:

const func = () => {}

export default func;

При этом снаружи это имя никак не используется, любой экспорт по умолчанию импортируется под любым именем.

Синтаксис экспорта по умолчанию таков, что после обязательных ключевых слов export default можно указать выражение, будь то константа:

const favColour = 'red';

export default favColour;

или анонимная функция:

export default () => 'Hello, world!';
/*
вариант для функции типа 'Function Expression'
export default function() {
  return 'Hello, world!';
}
*/

или даже просто литерал, например:

export default 'Hello, world!';

В отличие от именованного экспорта здесь после ключевых слов недопустимо указывать объявления переменных (с использованием const, let, var) и возможен только один дефолтный экспорт на модуль.

Экспорт и импорт в действии

donor.js - файл, в котором размещены экспортные функции. acceptor.js - файл, в котором происходит импорт функций.

Давайте наполним donor.js экспортными константами и функциями. Для полноты картины к именованному экспорту мы добавили ещё и экспорт по умолчанию (это допустимо, хотя и не принято так делать, по смыслу экспорта по умолчанию он должен быть единственным экспортом на модуль):

// donor.js
export const getFirstWeekday = () => 'Monday';
export const getSecondWeekday = () => 'Tuesday';
/* экспортируем функцию типа 'Function Declaration' */
export function getThirdWeekday () {
  return 'Wednesday';
}
export const fourthWeekday = 'Thursday';
const getFifthWeekday = () => 'Friday';
export default getFifthWeekday // export default () => 'Friday'
/* две функции ниже не будут экспортированы */
const firstWeekendDay = 'Saturday';
const getLastWeekendDay = () => 'Sunday';

/*
аналогичный альтернативный вариант экспорта заключается в том, что не надо помечать функции и
переменныe ключевым словом 'export' ('default export'), вместо этого достаточно написать
следующее выражение (сформировать объект для экспорта):
export { getFirstWeekday, getSecondWeekday, getThirdWeekday, fourthWeekday, getFifthWeekday as default };
*/

Теперь рассмотрим несколько вариантов, как можно импортировать эти переменные и функции (далее для краткости - функции) в модуль acceptor.js:

---> Импортируем все функции из donor.js в виде объекта, которому необходимо дать произвольный (соответствующий общим правилам именования переменных) псевдоним. Обращаемся к импортированным функциям как к свойствам этого объекта:

// acceptor.js
import * as someName from './donor'; // имя модуля можно указывать без расширения

// посмотрим, что из себя представляет импортированный объект someName
console.log(someName);
/*
{ getThirdWeekday: [Function: getThirdWeekday],
  getFirstWeekday: [Function: getFirstWeekday],
  getSecondWeekday: [Function: getSecondWeekday],
  fourthWeekday: 'Thursday',
  default: [Function: getFifthWeekday]
}
*/
someName.getFirstWeekday(); // 'Monday'
someName.getThirdWeekday(); // 'Wednesday'
someName.fourthWeekday; // 'Thursday'
someName.default(); // доступ (при данном виде импорта) к значению по умолчанию: 'Friday'
someName.firstWeekendDay; // undefined, такого свойства не существует, потому что переменная не была экспортирована

---> Сделаем выборочный импорт функций из donor.js. Для этого перечислим в фигурных скобках имена интересующих нас функций. Обращаемся к импортированным функциям по этим именам. При этом им можно давать псевдонимы, далее в текущем модуле доступ к таким функциям будет происходить только по псевдониму (это может пригодится в том случае, если в модуле уже существует функция с таким именем, для разрешения конфликта имён):

// acceptor.js
import { getFirstWeekday, getThirdWeekday, fourthWeekday as fourthDay } from './donor';

// константа с именем 'fourthWeekday' уже существует в модуле
const fourthWeekday = 'This is Thursday';

getFirstWeekday(); // 'Monday'
getThirdWeekday(); // 'Wednesday'
fourthDay; // 'Thursday'
fourthWeekday; // 'This is Thursday'

---> Импортируем из модуля donor.js значение по умолчанию. Для этого достаточно просто указать после ключевого слова import произвольное (наиболее подходящее и удобное) имя. В таком случае по этому имени можно будет обращаться к значению по умолчанию из импортируемого модуля:

// acceptor.js
import nameOfDefault from './donor';

// как вы помните, значением по умолчанию модуля donor.js является функция, возвращающая строку 'Friday'
nameOfDefault(); // 'Friday'

Также есть возможность импортировать одновременно и значение по умолчанию и другие экспортированные компоненты. Это можно сделать несколькими способами, приведём примеры:

// acceptor.js
// важно: имя значения по умолчанию стоит первым и не заключено в фигурные скобки
import nameOfDefault, { getFirstWeekday, getSecondWeekday } from './donor';

// как вы помните, значением по умолчанию модуля donor.js является функция, возвращающая строку 'Friday'
nameOfDefault(); // 'Friday'
getFirstWeekday(); // 'Monday'
getSecondWeekday(); // 'Tuesday'

тот же самый результат можно получить ещё одним альтернативным способом:

// acceptor.js
import { default as nameOfDefault, getFirstWeekday, getSecondWeekday } from './donor';

// как вы помните, значением по умолчанию модуля donor.js является функция, возвращающая строку 'Friday'
nameOfDefault(); // 'Friday'
getFirstWeekday(); // 'Monday'
getSecondWeekday(); // 'Tuesday'

<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».