JavaScript в действии: подключение и базовый синтаксис
Основные способы интеграции JavaScript
Использование JavaScript начинается с выбора способа его подключения к HTML и понимания базовых синтаксических конструкций. Ниже рассмотрены варианты от простого встраивания до современных модулей, а также типичные ошибки.
Наиболее эффективное решение: внешний файл с атрибутом defer
Атрибут defer позволяет загружать скрипт параллельно с парсингом HTML, но выполнять его только после полной загрузки документа. Это не блокирует отображение и гарантирует, что DOM доступен. Порядок выполнения нескольких скриптов с defer сохраняется.
Пример: подключение app.js с defer
<!DOCTYPE html>
<html lang="ru">
<head>
<script src="app.js" defer></script>
</head>
<body>
<p id="message">Загрузка...</p>
</body>
</html>использовать javascript (использование javascript)
// app.js
console.log('Скрипт выполнен после полной загрузки HTML');
document.getElementById('message').textContent = 'JavaScript работает!';Скрипт выполнен после полной загрузки HTML (на странице текст изменится на 'JavaScript работает!')
Пояснение: браузер загружает app.js параллельно, но запускает его только после того, как весь HTML разобран. Это исключает ошибки вроде обращения к ещё несуществующим элементам.
Типичные ошибки и их решения:
- Ошибка: скрипт без
deferв<head>блокирует парсинг, страница может отображаться с задержкой. Решение: используйтеdeferили размещайте скрипт перед закрывающим</body>. - Ошибка:
document.querySelectorвозвращаетnull, если скрипт выполняется до появления элемента в DOM. Решение: оборачивайте код в обработчикDOMContentLoadedили используйтеdefer.
Как добавить небольшой фрагмент кода прямо в HTML?
Встроенный скрипт (inline) удобен для быстрых тестов или однострочников. Код помещается внутрь тега <script>.
<script>
console.log('Привет, мир!');
</script>Проблемы:
- При большом объёме кода страница становится громоздкой, теряется читаемость.
- Код не кешируется браузером, что увеличивает объём загрузки при каждом запросе.
Решение:
Для повторного использования или логики объёмом более нескольких строк лучше выносить код во внешний файл.Как подключить синхронный внешний скрипт, если defer не подходит?
Иногда требуется выполнить скрипт до завершения загрузки страницы (например, для отображения рекламы или аналитики). Тогда атрибут defer не используется, а скрипт помещают в <head> или <body>. Однако это блокирует парсинг.
<head>
<script src="sync.js"></script>
</head>Типичная ошибка:
Если скрипт пытается обратиться к элементу, которого ещё нет в DOM, возникает ошибка TypeError: Cannot read properties of null.
Решение:
Перенесите скрипт в конец<body> или используйте DOMContentLoaded внутри самого скрипта, если это возможно.Async или defer: когда какой выбирать?
Оба атрибута загружаются параллельно, но async выполняет скрипт сразу после загрузки, не дожидаясь остальных скриптов или DOM. defer ждёт окончания парсинга и соблюдает порядок.
<script src="analytics.js" async></script>
<script src="app.js" defer></script>В этом примере analytics.js может выполниться раньше, чем app.js, даже если он подключён позже. defer гарантирует, что app.js запустится после полной загрузки HTML.
Ошибки:
- Использование
asyncдля скриптов, зависящих от DOM, часто приводит к тем же проблемам сnull. - Скрипты с
async, зависящие друг от друга, могут выполняться в непредсказуемом порядке.
Решение:
Для скриптов, которые работают с DOM или требуют определённого порядка, всегда используйтеdefer. async подходит для независимых скриптов (счётчики, трекеры).Расширенные примеры использования JavaScript
1. Модульная архитектура с import/export
Современный JavaScript поддерживает модули (type="module"). Код разбивается на файлы, импортирующие нужные функции. Модули автоматически загружаются с defer-подобным поведением.
// math.js
export function sum(a, b) {
return a + b;
}// main.js
import { sum } from './math.js';
console.log('Сумма 5 и 3:', sum(5, 3));<script type="module" src="main.js"></script>Сумма 5 и 3: 8
Пояснение: браузер загружает модули параллельно, выполнение происходит после разбора HTML. Модули автоматически находятся в строгом режиме и не загрязняют глобальную область видимости.
2. Динамическая загрузка скрипта через createElement
Иногда скрипт нужно подгрузить только при определённом действии пользователя, например, после клика. Это экономит трафик.
document.getElementById('loadBtn').addEventListener('click', () => {
const script = document.createElement('script');
script.src = 'widget.js';
script.onload = () => console.log('Виджет загружен');
document.body.appendChild(script);
});После клика на кнопку в консоли появляется 'Виджет загружен', когда файл загрузится и выполнится.
Пояснение: динамическое добавление скрипта не блокирует страницу, позволяет отложить тяжёлые библиотеки до момента их реальной необходимости.
3. Асинхронные запросы с async/await и fetch
Для получения данных с сервера без перезагрузки страницы используют fetch в связке с async/await.
async function loadUsers() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
console.log('Пользователи:', users);
} catch (error) {
console.error('Ошибка загрузки:', error);
}
}
loadUsers();Пользователи: [объект пользователя 1, объект пользователя 2, ...]
Пояснение: код ожидает ответа от сервера и затем выводит массив объектов. Блок catch обрабатывает проблемы сети или неверный URL.
4. Работа с замыканиями для создания приватных переменных
Замыкание позволяет сохранять состояние между вызовами функций, не загрязняя глобальную область.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 31 2 3
Пояснение: переменная count доступна только внутри возвращаемой функции - это пример инкапсуляции данных.
5. Деструктуризация и spread оператор для удобной работы с массивами и объектами
Эти приёмы сокращают код и делают его более читаемым.
// Деструктуризация объекта
const user = { name: 'Анна', age: 25 };
const { name, age } = user;
console.log(name, age); // Анна 25
// Spread оператор для объединения массивов
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1,2,3,4,5,6]
// Копирование объекта с изменением
const updatedUser = { ...user, age: 26 };
console.log(updatedUser); // { name: 'Анна', age: 26 }Анна 25
[1,2,3,4,5,6]
{ name: 'Анна', age: 26 }Пояснение: деструктуризация извлекает свойства в отдельные переменные, spread позволяет легко копировать и объединять данные без мутации исходных.