Написание более чистого JavaScript с модулями
Хотите писать более чистый код? В этой статье рассматриваются некоторые приемы, которые можно использовать для написания поддерживаемого и простого для понимания кода JavaScript.
Модули — одна из наиболее часто используемых функций JavaScript, поскольку большинство фреймворков и библиотек JavaScript используют функцию модуля для организации и разделения на компоненты. Некоторые разработчики считают, что ключевые слова import и export — это особенности ReactJS.
В этой статье я объясню, как инкапсулировать код с помощью модулей, чтобы сделать ваши проекты чище. Давайте посмотрим, что такое инкапсуляция в следующем разделе.
Инкапсуляция
В программировании инкапсуляция относится к объединению связанного кода в единый источник. Код может включать связанные функции и переменные внутри файлов или связанные файлы внутри папки.
Инкапсуляция используется для ограничения прямого доступа к данным и реализациям связанного и связанного кода из кода, который их использует. Таким образом, реализация функций скрыта, не может быть изменена другими частями кода и будет меняться только тогда, когда вы захотите ее изменить. Например, в приложении для блога, когда вы объединяете все свойства и методы записи в один модуль, вам нужно указать только код, который должен с ним взаимодействовать: имена функций и значения, необходимые для их работы.
Инкапсуляция необходима, потому что она делает ваш код чище, удобнее в сопровождении и легче для понимания, повторного использования и тестирования.
Далее я объясню, какие модули есть в JavaScript.
Модули
Модуль — это независимая, автономная и отделяемая единица программы. JavaScript позволяет структурировать большие программы и кодовые базы, создавая модули кода, содержащие связанные функции и свойства, которые можно экспортировать в несколько других файлов, которым нужны эти свойства и функции.
Организация кода — основная причина, по которой фреймворк является предпочтительным вариантом для большинства разработчиков при создании средних и больших приложений. Я покажу вам, как структурировать ваш код с помощью модулей JavaScript. Во-первых, давайте посмотрим на синтаксис модулей JavaScript.
Экспорт
Использование модулей делает функциональные возможности доступными для других модулей; ключевое слово export
делает это возможным. Вы можете сделать функцию доступной для других модулей:
export function verifyUser(email, password) {
// do all necessary checks
return "Successfully verified user!"
}
Файлы, которые импортируют или экспортируют код, называются модулями.
Вышеупомянутая функция готова к использованию в других модулях, которым нужна функция verifyUser
. Это называется именованным экспортом.
Экспорт нескольких свойств
Используя ключевое слово export
, вы можете экспортировать что угодно, от переменных до классов. Чтобы экспортировать несколько свойств из модуля, вам необходимо указать перед объявлением ключевое слово export
:
export const userName = "Lee";
export const userAge = 30;
export const user = {
name: "Lee",
age: 30
};
Приведенный выше код экспортирует все объявления в модуле, но вам не нужно экспортировать все в модуле; у вас могут быть объявления, которые доступны только для использования внутри модуля:
const apiKey = "12345";
export function getApiKey() {
return apiKey;
}
Объявление — это функция, класс, переменная или что-то еще, объявленное внутри модуля.
Приведенный выше код экспортирует только функцию getApiKey
, которая возвращает переменную apiKey
, объявленную над функцией.
Более чистый способ экспорта нескольких свойств — использование нотации фигурных скобок:
// user sign in
function userSignIn() {
console.log("User signed in");
}
// user sign out
function userSignOut() {
console.log("User signed out");
}
// delete task
function deleteTask(id) {
console.log(`Task ${id} deleted`);
}
//add task
function addTask(task) {
console.log(`Task ${task.id} added`);
}
//edit task
function editTask(id, changes) {
console.log(`Task ${id} edited`);
}
//complete task
function completeTask(id) {
console.log(`Task ${id} completed`);
}
Вы можете экспортировать все функции в блоке кода выше:
export {
userSignIn,
userSignOut,
deleteTask,
addTask,
editTask,
completeTask
};
Приведенный выше блок кода экспортирует все объявления внутри фигурных скобок и доступен для импорта в другие модули, так что теперь модуль будет выглядеть так:
// user sign in
function userSignIn() {
console.log("User signed in");
}
// user sign out
function userSignOut() {
console.log("User signed out");
}
// delete task
function deleteTask(id) {
console.log(`Task ${id} deleted`);
}
//add task
function addTask(task) {
console.log(`Task ${task.id} added`);
}
//edit task
function editTask(id, changes) {
console.log(`Task ${id} edited`);
}
//complete task
function completeTask(id) {
console.log(`Task ${id} completed`);
}
export {
userSignIn,
userSignOut,
deleteTask,
addTask,
editTask,
completeTask
};
Блок кода export
{...} обычно размещается в нижней части модуля для удобочитаемости, но вы можете поместить его в любое место внутри модуля.
Экспорт по умолчанию
Иногда у вас может быть функция, состоящая из более чем 100 строк кода, и вы хотите разместить ее отдельно в одном файле. Чтобы упростить его импорт в другие модули, вы можете сделать его экспортом по умолчанию:
// google sign in
export default function googleSignIn() {
// 100 lines of checking and getting details
console.log("User signed in with Google");
}
Вышеупомянутая функция экспортируется из модуля по умолчанию. В следующем разделе я покажу вам, как использовать ключевое слово import.
Примечание. Модуль может иметь только один экспорт по умолчанию (default
).
Импорт
В предыдущем разделе вы узнали об использовании ключевого слова export
, чтобы сделать свойства модуля доступными для других модулей. В этом разделе я научу вас, как использовать ключевое слово import
для получения кода из других модулей.
Импортирующий
Чтобы использовать код из других модулей, вы можете импортировать их с помощью ключевого слова import
:
import { userSignIn, userSignOut } from "./filePath.js";
Примечание. «./filePath
» — это относительный путь к маршруту каталога.
Приведенный выше код импортирует функции userSignIn и userSignOut из объявленного модуля. Вы можете импортировать одно или несколько объявлений; единственное требование — убедиться, что свойство определено в модуле, из которого вы импортируете.
Импорт экспортов по умолчанию
В разделе «Экспорт по умолчанию» выше вы узнали, как экспортировать функции по умолчанию из модулей. В этом разделе объясняется, как импортировать экспортированные декларации по умолчанию.
Вы можете импортировать экспорт по умолчанию, используя следующее:
import googleSignIn from "./filePath.js";
Приведенный выше код импортирует функцию googleSignIn
, которая была экспортирована по умолчанию в предыдущем разделе. Поскольку модуль может иметь только один экспорт по умолчанию, вы можете опустить имя объявления функции, и приведенный выше код все равно будет работать; это означает, что вы можете объявить функцию без имени:
// google sign in
export default function () {
// 100 lines of checking and getting details
console.log("User signed in with Google");
}
Приведенный выше код будет работать, потому что это экспорт по умолчанию.
Единственная разница между импортом по умолчанию и именованным экспортом заключается в фигурных скобках:
// default export, no braces
import googleSignIn from "./filePath.js";
// named export, must use braces
import { userSignIn, userSignOut } from "./filePath.js";
Импорт пространства имен
Иногда у вас есть модуль, содержащий множество различных служебных функций, и вы хотите использовать одно имя для доступа к ним; это имя называется пространством имен (namespace
).
Например, вы определили все функции, связанные с пользователем, в модуле:
function getUserName() {
return userName;
}
function getUserAge() {
return userAge;
}
function getUser() {
return user;
}
function getApiKey() {
return apiKey;
}
function userSignIn() {
console.log("User signed in");
}
function userSignOut() {
console.log("User signed out");
}
export {
getUserName,
getUserAge,
getUser,
getApiKey,
userSignIn,
userSignOut,
};
Затем модуль, который будет использовать эту функцию, импортирует ее:
import * as userFuncs from './filePath.js';
В приведенном выше коде используется специальный символ * для импорта всех объявлений в модуле поверх userFuncs
. Вы можете получить доступ к getUserName
в модуле:
import * as userFuncs from './filePath.js';
// use getUserName function
userFuncs.getUserName();
Переименование объявлений
Чтобы помочь разработчикам избежать конфликтов имен, модули JavaScript используют ключевое слово as для переименования объявлений.
Переименование экспорта
Иногда у вас может быть объявление с именем login, и вы используете другую библиотеку, в которой есть функция с именем login; вы можете экспортировать свою функцию входа в систему как myLogin:
function login(email, password) {
// check if your email and password are valid
return "User logged in";
}
// export as myLogin
export { login as myLogin };
Приведенный выше код объявляет функцию входа в систему, но экспортирует ее как myLogin. Вы можете импортировать функцию как myLogin:
import { myLogin } from "./filePath.js";
Вышеупомянутая функция импортирует функцию myLogin
. Далее я покажу вам, как переименовать импорт.
Переименование импорта
При работе над большим проектом вы будете импортировать из нескольких модулей, что упрощает перепутывание имен объявлений. Например, вы можете работать с двумя разными библиотеками, одна для аутентификации в Twitter, а другая для аутентификации в Google, и обе имеют собственную функцию входа в систему. Чтобы избежать коллизий имен, в этом случае вы можете импортировать их с разными именами:
// import twitter login
import login as twitterLogin from 'twitter-auth';
// import google login
import login as googleLogin from 'google-auth';
Приведенный выше код импортирует функцию входа в систему двух разных библиотек с определенными именами. Так легче избежать ошибок и помочь другим разработчикам понять ваш код.
Далее я покажу вам, как реэкспортировать декларацию.
Реэкспорт
Хотя модули JavaScript используются редко, они позволяют повторно экспортировать модуль, который вы ранее импортировали:
// import the login function
import { login } from './filePath.js';
// re-export the login function
export { login };
Приведенный выше код импортирует функцию входа в систему, а затем реэкспортирует ее.
Теперь, когда вы знаете, как использовать импорт и экспорт, я покажу вам, как структурировать приложения с помощью модулей.
Структурирование кода с помощью модулей
В предыдущих разделах вы узнали, как использовать ключевые слова import и export, чтобы сделать код доступным в разных и нескольких модулях. В этом разделе я объясню преимущества использования модулей и то, как они помогают структурировать ваш код и приложения.
Возможность повторного использования
Независимо от того, являетесь ли вы начинающим, средним или продвинутым разработчиком, вы, вероятно, видели термин «СУХОЙ» или «Не повторяйтесь» в Интернете.
Это означает, что в большинстве случаев вы можете повторно использовать функции несколько раз в разных частях кода. Как вы узнали из предыдущих разделов, модули упрощают эту задачу, поскольку все, что вам нужно сделать, это написать код, экспортировать его, а затем использовать в других модулях, которым нужна конкретная функция.
Некоторые из преимуществ этого подхода заключаются в следующем:
- Экономит время.
- Повышает ремонтопригодность и переносимость кода.
- Повышает производительность разработчиков.
- Уменьшает избыточность.
Это всего лишь несколько преимуществ повторного использования, которых можно достичь с помощью модулей.
Компонуемость
Компонуемость позволяет разбивать функциональность на части и объединять их для формирования целостной функции, а также позволяет повторно использовать части функции в других частях приложения.
Примером этого является то, что при создании функции addComment
вы можете сделать некоторые проверки внутри функции:
- Разрешено ли этому пользователю оставлять комментарии?
- Удалите из ввода запрещенные символы, такие как <h1></h1>.
- Длина этого ввода больше разрешенных символов?
- Добавьте ввод в базу данных.
Затем вы можете создать эти четыре разные функции, например:
// check if user if allowed to comment
export default function canComment(user) {
// make checks here
return user.signedIn;
}
// check if input contains html tags
export function containsHTML(input) {
return /<[a-z][\s\S]*>/i.test(input);
}
// check if input is not longer than maxLength
export function isTooLong(input, maxLength) {
return input.length > maxLength;
}
// add input to the database
export function addToDatabase(input) {
console.log(`${input} added to database`);
}
Вышеупомянутые функции теперь можно комбинировать для создания функции addComment
:
function addComment(user, comment) {
if (canComment(user) && !containsHTML(comment) && !isTooLong(comment)) {
addToDatabase(comment);
}
}
Каждая функция, входящая в состав функции addComment
, также может использоваться независимо в других частях программы.
Преимущества компонуемости заключаются в следующем:
- Это делает ваш код чище.
- Это упрощает повторное использование существующего кода.
- Это облегчает разделение проблем.
- Это упрощает понимание кода.
Изоляция
Понимание всего проекта может быть трудным для новых членов команды, работающих над большим проектом.
Поскольку модули позволяют создавать приложение путем составления небольших целенаправленных функций, каждую из этих функций можно создавать, исправлять и рассматривать отдельно.
Используя пример из предыдущего раздела, чтобы изменить реализацию, чтобы проверить, может ли пользователь комментировать, вам нужно только изменить функцию canComment. Остальное можно оставить без изменений.
Изоляция упрощает понимание, изменение и тестирование кода.
Читабельность
Использование модулей в вашем коде облегчает чтение. Это особенно необходимо при работе с большими приложениями, и почти невозможно объяснить каждому разработчику в команде, что вы пытаетесь сделать с помощью функции.
Например, не заходя в каждый файл, чтобы увидеть реализацию, разработчик почти автоматически знает, что делает следующая функция:
function addComment(user, comment) {
if (canComment(user) && !containsHTML(comment) && !isTooLong(comment)) {
addToDatabase(comment);
}
}
Приведенный выше код можно прочитать так: «Если пользователь может комментировать, комментарий не содержит HTML и комментарий не слишком длинный, добавьте комментарий в базу данных». Это упрощает участие новых членов команды в проекте, что экономит время.
Организация
При использовании модулей организация происходит почти автоматически, поскольку каждая часть кода изолирована.
Например, у вас могут быть все функции, используемые для проверки типов объявлений внутри typeUtils.js
:
// check if input is a string
export function isString(input) {
return typeof input === "string";
}
// check if input is a number
export function isNumber(input) {
return typeof input === "number";
}
// check if input is an array
export function isArray(input) {
return Array.isArray(input);
}
// check if input is an object
export function isObject(input) {
return typeof input === "object";
}
// check if input is a function
export function isFunction(input) {
return typeof input === "function";
}
// check if input is a boolean
export function isBoolean(input) {
return typeof input === "boolean";
}
// check if input is null
export function isNull(input) {
return input === null;
}
Не задумываясь, приведенный выше код организован так, как все они связаны и независимы друг от друга.
Вывод
Надеюсь, вам понравился этот урок! Надеюсь, вы лучше понимаете, как использование модулей в JavaScript может улучшить ваш код. В этой статье вы узнали, что такое инкапсуляция, что такое модули и как они работают, а также узнали, как работают ключевые слова экспорта и импорта и как переименовывать объявления. Наконец, вы узнали, как модули могут помочь структурировать ваш код.