Что умеет браузер: window, DOM и BOM
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Язык вы уже знаете — теперь у него появляется среда обитания. Вокруг вашего кода в браузере лежит целый мир: объект window, дерево страницы и десятки готовых интерфейсов, которых в самом языке нет и никогда не было.
У вашего кода в браузере есть хозяин — объект window. Он одновременно и окно вкладки, и глобальный объект: всё, что объявлено глобально, и всё, что даёт браузер, становится его свойством. Поэтому document и window.document — одно и то же имя, просто префикс обычно не пишут.
Из этого следует неприятное: глобальная область в браузере — не пустая комната, а чужая квартира с мебелью. Ваше глобальное имя может совпасть с уже существующим свойством окна, и тогда победит не ваш код.
У window заранее заняты совсем обычные имена: name, length, top, status, closed. Присвойте глобально name = 42 — и прочитаете обратно строку «42»: за этим именем стоит браузерный сеттер, который приводит значение к строке. Ещё один довод не заводить глобальные переменные вообще.
Получив HTML, браузер не хранит его как текст. Он разбирает разметку и строит дерево объектов — DOM, Document Object Model. Корень дерева лежит в document, ниже идут элементы, текстовые узлы и комментарии; у каждого узла свои свойства и методы.
Дерево живое. Меняете свойство объекта — страница перерисовывается, добавляете узел — он появляется на экране. Обратной связи с исходным HTML нет: исходник давно прочитан и забыт, вы работаете с деревом.
Строку можно отдать браузеру, но обратно вы получите узлы, а не текст: парсер отрабатывает сразу же.
DOM отвечает за содержимое страницы. Всё, что вокруг неё — адрес, история переходов, сведения о браузере и экране — собрано под неофициальным именем BOM, Browser Object Model. Спецификации ровно с таким названием не существует; это просто удобная граница между «страницей» и «окном».
Главные жильцы: location — адрес, разобранный на части; history — кнопки «назад» и «вперёд»; navigator — паспорт браузера и точка входа во многие возможности устройства; screen — размеры экрана. Устроен location так же, как обычный URL, поэтому его удобно изучать на любом адресе.
Сам язык даёт синтаксис и скромный набор встроенных объектов: Object, Array, Math, JSON, Promise. Всё остальное, чем вы пользуетесь в браузере — DOM, события, таймеры, fetch, хранилища, звук, камера — это Web API: интерфейсы, которые хост подкладывает в тот же глобальный объект.
Типичный Web API — объект с методами и событиями. И событие не обязано приходить от человека: вызов element.click() или dispatchEvent запускает ровно ту же цепочку обработчиков, что и настоящее нажатие. На этом держатся и отладка, и тесты.
Один и тот же движок исполняет JavaScript и в Chrome, и в Node.js — но окружения у них разные. В Node нет ни window, ни document: там нечего рисовать. Зато есть файловая система, процессы и сеть без ограничений браузера.
| Что | Браузер | Node.js |
|---|---|---|
| Глобальный объект | window | global |
| Общее имя для обоих | globalThis | globalThis |
| Страница | document, элементы, события | DOM отсутствует |
| Файлы | только то, что дал пользователь | модуль node:fs, весь диск |
| Сеть | fetch с правилами CORS | любые сокеты, CORS не действует |
| Где выполняется код | на чужом устройстве | на вашем сервере |
Отсюда практическое правило: библиотека, которая трогает document, не заведётся на сервере, а та, что читает файлы через node:fs, не соберётся для браузера. Универсальный код пишут через globalThis и проверку возможностей.
Ни один учебник не перечислит все Web API — их сотни, и список растёт. Зато окружение отвечает на вопросы само: наберите в консоли DevTools document. и получите список свойств, а console.dir(document.body) покажет элемент как объект, а не как разметку.
Такая проверка называется feature detection: если API нет, включают запасной путь, а не падают. Консоль, правда, знает только про ваш браузер — на вопрос «а есть ли это в Safari» отвечают MDN и caniuse.com. Короткий ответ на страницах MDN — статус Baseline, разобранный в прологе: Widely available — пишите; Newly available — знайте, что у части людей браузер старее; Limited availability — без запасного пути не рассчитывайте.
Элемент — обычный объект с прототипной цепочкой. Чем конкретнее тип, тем больше он умеет: у input есть value, а общие для всех узлов методы лежат выше по цепочке.
- window — глобальный объект браузера: и ваши глобальные имена, и всё, что даёт браузер, лежат его свойствами.
- DOM — про содержимое страницы: document и дерево живых узлов, не связанное с исходным текстом HTML.
- BOM — неофициальное имя для всего вокруг страницы: location, history, navigator, screen.
- Web API — интерфейсы от хоста (события, таймеры, fetch, хранилища); к языку они не относятся, и в Node их может не быть — общее имя глобального объекта для обоих миров — globalThis.
- Что доступно, спрашивают у консоли: проверка «имя» in window, console.dir и прототипы элемента; кроссбраузерность — на MDN и caniuse.
1/3Что из списка даёт браузер, а не сам язык JavaScript?
2/3Куда относят объект location, хранящий адрес страницы?
3/3Модуль должен работать и в браузере, и в Node. Как получить глобальный объект?
Паспорт элемента
На подложке лежит карточка. Допишите код так, чтобы он вывел число абзацев внутри неё и текст второго абзаца.
Заголовок h3 — тоже элемент, поэтому children здесь не подойдёт: нужен запрос именно по абзацам.
querySelectorAll возвращает не массив, а NodeList: индексы и forEach у него есть, а map нет — если нужен массив, оберните в Array.from.
Кнопка, которую нажимает код
Повесьте на кнопку обработчик click, который увеличивает число в output на единицу. Затем нажмите кнопку из кода три раза — так, чтобы в консоль вывелось "3".
Состояние удобнее держать в переменной, а в DOM только показывать: textContent всегда строка, и считать в ней неудобно.
Метод click() вызывает настоящее событие: оно всплывает и доходит до всех обработчиков — так же сработал бы и tick.dispatchEvent(new MouseEvent("click")).