Раздел 7 · Браузер и Frontend

JavaScript в браузере и подключение скриптов

junior~30 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

JavaScript в браузере и подключение скриптов

Этот урок открывает фронтенд-серию. Он не про React, не про сборщики и не про CSS-фреймворки. Он про то, где живёт JS в браузере, какие у него глобалы, как подключаются скрипты и почему <script src=...> без атрибутов в 2026 году пахнет легаси.

Если ты уже год пишешь на React, скорее всего ты можешь объяснить useEffect, но не объяснишь, чем defer отличается от async. Этот зазор лечим сразу: всё, что выше React, опирается на эти знания.

1. Один глобал на одну вкладку

Открыл вкладку, получил один window. Открыл вторую, получил второй. Они не делят память напрямую: разные вкладки общаются через BroadcastChannel, localStorage-события, сервис-воркер или серверный API. Изнутри JS-кода window это глобальный объект: всё, что объявлено без let/const/var в верхнем скоупе, попадает в его владение.

console.log(window === globalThis); // true в браузере
console.log(window.innerWidth); // 1440 или сколько у тебя

// "обнажённое" присваивание создаёт свойство window
foo = 42;
console.log(window.foo); // 42

В Node globalThis тоже есть, но это не window. Это global, с другим набором свойств: нет document, нет location. Есть некоторые общие API (setTimeout, Promise, URL, fetch), но не все.

2. BOM: окно и его соседи

Браузерная объектная модель (Browser Object Model) это набор объектов, которые описывают саму вкладку. Не контент страницы (это DOM, тема урока 02), а вокруг.

ОбъектЧто внутри
windowглобал, размеры окна, таймеры, фокус
documentформально DOM, но висит на window.document
locationURL текущей страницы, href, pathname, search, hash
historyстек переходов, pushState, replaceState, back, forward
navigatorбраузер и платформа, userAgent, language, clipboard, serviceWorker
screenфизический экран, width, height, pixelDepth

Это всё, что нужно знать про BOM для начала. Большая часть твоего кода не будет напрямую трогать window.screen, но location.pathname и history.pushState лежат в основе любого SPA-роутера.

// Навигация без перезагрузки страницы. Это основа React Router.
history.pushState({ from: 'chat' }, '', '/channels/general');
// URL поменялся, но страница не перезагрузилась.

// Слушаем кнопку "Назад".
window.addEventListener('popstate', (event) => {
  console.log('back to', location.pathname, event.state);
});

3. Как подключать скрипты

Тег <script> существует с 1995 года и за это время оброс правилами. Сегодня правильно одно из трёх:

<!-- 1. Классический script с defer -->
<script defer src="/app.js"></script>

<!-- 2. ES-модуль (defer по умолчанию) -->
<script type="module" src="/app.js"></script>

<!-- 3. importmap, чтобы маппить bare imports без сборщика -->
<script type="importmap">
  {
    "imports": {
      "lodash": "https://cdn.skypack.dev/lodash"
    }
  }
</script>
<script type="module">
  import { chunk } from 'lodash';
  console.log(chunk([1, 2, 3, 4], 2));
</script>

Что делает каждое поведение:

  • <script src=...> без атрибутов. Блокирует HTML-парсер. Браузер останавливается, качает файл, выполняет, продолжает парсинг. Единственный кейс, где это допустимо, это критичный inline-скрипт без src. Пример: чтение темы из localStorage до первой отрисовки (first paint), чтобы не было вспышки белого.
  • <script defer src=...>. Скачивается параллельно с парсингом, выполняется после DOMContentLoaded, сохраняя порядок между defer-скриптами. Это самый предсказуемый вариант для классического приложения.
  • <script async src=...>. Скачивается параллельно, выполняется как только скачался, без гарантии порядка. Подходит только для независимых скриптов (аналитика, рекламный пиксель), которым плевать на остальной код.
  • <script type="module">. ES-модули в браузере. Подразумевает defer, поддерживает import, выполняется в строгом режиме. Это дефолт для новых приложений.
  • <script type="importmap">. Не код, а словарь. Позволяет писать import x from 'lodash' без сборщика: браузер сам зарезолвит по таблице.

Слова про порядок и блокировку проще увидеть, чем прочитать. Пощёлкай один и тот же набор из двух скриптов под тремя стратегиями: где парсер встаёт, в каком порядке выполняется код и куда попадает DOMContentLoaded.

Когда выполнится скрипт относительно парсинга
раньшевремяпозже

Правило: новый проект пишем на type="module". Inline-скрипт без src только для критичной инициализации тёмной темы. defer оставляем для legacy-страниц, где type="module" нельзя из-за поддержки старых браузеров.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.