«Собери свой код по кусочкам»: почему нельзя просто вставить один JS-файл в другой
Каждый, кто писал на JavaScript больше пары недель, рано или поздно упирается в стену: «А как мне использовать функции из другого файла? Не копировать же их туда-сюда?!» Старый добрый Stack Overflow знает об этом не понаслышке — вопрос "How do I include a JavaScript file in another JavaScript file?" набирает тысячи просмотров каждый месяц. И это логично: в реальных проектах код быстро разрастается, и держать всё в одном файле — путь к хаосу.
В этой статье мы разберем 5 проверенных способов подключить один JS-файл внутри другого. От древних методов до современных стандартов. Поехали!
Метод 1: Классический <script> в HTML
Самый простой и доисторический способ — просто добавить несколько тегов <script> в ваш HTML-файл. Но есть нюанс: файлы загружаются последовательно. Если ваш main.js зависит от utils.js, то utils.js должен быть выше.
<!DOCTYPE html>
<html>
<head>
<script src="utils.js"></script>
<script src="main.js"></script>
</head>
<body>
...
</body>
</html>
Проблема: Это не подключение JS-файла внутри другого JS-файла, а просто порядок загрузки. Если у вас 20 скриптов, вы рискуете запутаться в зависимостях. Кроме того, все переменные и функции становятся глобальными — привет, конфликты имен!
Метод 2: ES6 Modules (современный стандарт)
Начиная с ES6 (ECMAScript 2015), в JavaScript появилась встроенная модульная система. Это самый правильный способ включить один JS-файл в другой в современных проектах.
Экспорт из файла (math.js)
// math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
export default function multiply(a, b) {
return a * b;
}
Импорт в другой файл (app.js)
// app.js
import multiply, { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
console.log(multiply(4, 5)); // 20
Важно: Чтобы это работало в браузере, в HTML нужно добавить атрибут type="module":
<script type="module" src="app.js"></script>
Этот метод решает все проблемы: изоляция кода, четкие зависимости, отсутствие глобальных переменных. Идеально для современных веб-приложений.
Метод 3: Node.js require (CommonJS)
Если вы пишете под Node.js, то стандарт — это CommonJS с функцией require(). Этот способ включить JS-файл в другой JS-файл используется в серверной разработке до сих пор (хотя ES6 модули уже пришли и туда).
Экспорт (utils.js)
// utils.js
function greet(name) {
return `Привет, ${name}!`;
}
const secret = 'myPassword123';
module.exports = {
greet,
secret
};
Импорт (server.js)
// server.js
const utils = require('./utils.js');
console.log(utils.greet('Алексей')); // Привет, Алексей!
console.log(utils.secret); // myPassword123
Просто, надежно, работает везде, где есть Node.js. Для браузеров — нет, только через сборщики (Webpack, Vite).
Метод 4: Динамическая загрузка скриптов (создаем script-тег)
Иногда нужно подключить JS-файл из другого JS-файла прямо во время выполнения. Например, когда вы подгружаете аналитику или плагин только по необходимости. Для этого можно динамически создать тег <script>.
// loadScript.js
function loadScript(src, callback) {
const script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Ошибка загрузки ${src}`));
document.head.appendChild(script);
}
// Использование
loadScript('https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js', (err, script) => {
if (err) {
console.error(err);
} else {
console.log('Lodash загружен!');
console.log(_.chunk([1, 2, 3, 4], 2)); // [[1, 2], [3, 4]]
}
});
Этот метод дает полный контроль: вы решаете, когда и какой файл загружать. Минус — нет гарантии порядка загрузки без колбэков или Promise.
Метод 5: Fetch + eval (экзотика, но работает)
Способ, который используют редко, но он существует. Вы можете прочитать содержимое другого JS-файла через fetch и выполнить его через eval().
// loader.js
async function includeJavaScript(url) {
const response = await fetch(url);
const code = await response.text();
eval(code);
}
// Использование
includeJavaScript('utils.js').then(() => {
console.log('Файл выполнен!');
// Теперь все функции из utils.js доступны
});
Предупреждение: eval() — это зло. Он медленный, небезопасный (XSS-атаки) и его сложно отлаживать. Используйте только для образовательных целей или в очень специфических ситуациях.
Что выбрать? Сравнительная таблица
- Простой статический сайт — используйте ES6 модули (type="module").
- Node.js проект — require (CommonJS) или ES6 модули.
- Динамическая загрузка (ленивая) — создание script-тега.
- Древний код / без сборщика — можно и теги script в HTML, но лучше перейти на модули.
- Эксперименты — fetch + eval (но осторожно).
Частые ошибки и как их избежать
- Несовместимость типов: Если используете ES6 модули в браузере, не забудьте
type="module". Без него будет ошибка. - Ошибка CORS при динамической загрузке: Скрипты с других доменов могут блокироваться. Используйте
crossoriginили загружайте с того же сервера. - Циклические зависимости: Когда файл A импортирует файл B, а B импортирует A. В ES6 модулях это решается, но лучше проектировать архитектуру без циклических связей.
- Глоб