Глобальные объекты Node.js: globalThis, Buffer, таймеры
✓ Все примеры выполнены на Node.js v22.23.1 (LTS), июль 2026
Содержание
В Node нет window, но есть свой набор объектов, доступных в любом файле без единого импорта. За последние годы этот набор сильно вырос: половина того, ради чего раньше ставили пакеты, теперь встроена в платформу.
globalThis вместо global
globalThis — стандартное имя глобального объекта, работающее везде. В Node оно указывает на тот же объект, что и старое global:
console.log('globalThis === global :', globalThis === global);
console.log('есть ли window :', typeof window);
globalThis === global : true
есть ли window : undefined
Разница только в переносимости: global понимает лишь Node, window — только браузер, а globalThis — обе среды.
Отдельно скажем очевидное: писать свои переменные в глобальный объект не нужно. Это выглядит удобно («доступ отовсюду»), но превращает модули в неявно связанные и ломает тесты. Нужен общий объект — экспортируйте его из модуля.
Что уже есть без импорта
for (const g of ['Buffer', 'URL', 'fetch', 'AbortController', 'structuredClone',
'queueMicrotask', 'setTimeout', 'setImmediate', 'console', 'TextEncoder']) {
console.log(` ${g.padEnd(16)}: ${typeof globalThis[g]}`);
}
Buffer : function
URL : function
fetch : function
AbortController : function
structuredClone : function
queueMicrotask : function
setTimeout : function
setImmediate : function
console : object
TextEncoder : function
| Объект | Зачем | Что заменил |
|---|---|---|
fetch |
HTTP-запросы | node-fetch, axios, request |
AbortController |
отмена операций по сигналу | самописные флаги отмены |
structuredClone |
глубокая копия | lodash.cloneDeep |
URL |
разбор и сборка адресов | устаревший url.parse() |
Buffer |
бинарные данные | — (специфика Node) |
TextEncoder |
строка ↔ байты | ручная возня с Buffer |
Практический вывод: прежде чем ставить зависимость ради мелкой утилиты, проверьте — возможно, она уже в платформе.
structuredClone: копия, которая не ломает Date
Классический трюк «глубокой копии» через JSON тихо портит данные: Date превращается в строку, Map и Set — в пустой объект, undefined исчезает.
const src = { d: new Date('2026-07-16'), m: new Map([['a', 1]]), nested: { x: [1, 2] } };
const clone = structuredClone(src);
clone.nested.x.push(3);
console.log('оригинал не изменился :', src.nested.x);
console.log('Date остался Date :', clone.d instanceof Date);
console.log('Map остался Map :', clone.m instanceof Map);
console.log('через JSON Date стал бы:', typeof JSON.parse(JSON.stringify(src)).d);
оригинал не изменился : [ 1, 2 ]
Date остался Date : true
Map остался Map : true
через JSON Date стал бы: string
Ограничение у structuredClone одно: функции и классы он не копирует — бросит DataCloneError. Для данных этого достаточно.
Порядок выполнения: замер, а не теория
Здесь живёт самое частое заблуждение. Запланируем всё сразу и посмотрим на фактический порядок.
setTimeout(() => order.push('setTimeout(0)'), 0);
setImmediate(() => order.push('setImmediate'));
Promise.resolve().then(() => order.push('промис'));
queueMicrotask(() => order.push('queueMicrotask'));
process.nextTick(() => order.push('process.nextTick'));
order.push('синхронный код');
Тот же код, запущенный в двух разных контекстах, даёт разный порядок:
чистый top-level : синхронный код -> process.nextTick -> промис -> queueMicrotask -> setImmediate -> setTimeout(0)
после top-level await : синхронный код -> промис -> queueMicrotask -> process.nextTick -> setImmediate -> setTimeout(0)
Первая строка — тот самый канонический порядок из учебников: nextTick идёт раньше промисов.
Вторая строка — то, о чём учебники молчат. Если код выполняется уже внутри промис-континуации (а именно так работает всё, что идёт после await), новые микрозадачи попадают в очередь, которая разбирается прямо сейчас, и успевают вперёд nextTick.
Практический вывод: не стройте логику на приоритете nextTick перед промисами. Он зависит от того, откуда вызван ваш код, а это меняется при любом рефакторинге, добавляющем await выше по стеку.
| Механизм | Когда выполнится | Для чего |
|---|---|---|
process.nextTick |
сразу после текущей операции | внутренние нужды Node; в прикладном коде почти не нужен |
queueMicrotask |
в очереди микрозадач | отложить на «сразу после текущего кода» |
Promise.then |
там же, где микрозадачи | обычная асинхронность |
setImmediate |
в фазе check событийного цикла | «после ввода-вывода» |
setTimeout(0) |
в фазе таймеров | «когда-нибудь скоро», не раньше |
setTimeout(fn, 0) не значит «немедленно»: ноль — это минимальная задержка, а не гарантия. Если нужно именно «после текущей операции», берите queueMicrotask или setImmediate.
- 1 Синхронный кодВыполняется весь, до последней строки
-
2
process.nextTickРаньше промисов — но только в этом контексте -
3
Микрозадачи
Promise.then,queueMicrotask— в порядке постановки -
4
setImmediateФаза check событийного цикла -
5
setTimeout(…, 0)Фаза таймеров. Ноль — минимум задержки, не гарантия
Таймеры возвращают объект, а не число
Отличие от браузера, на которое натыкаются: setTimeout в Node возвращает объект Timeout, а не числовой id.
const t = setTimeout(() => {}, 1000);
t.unref(); // не держать процесс живым ради этого таймера
clearTimeout(t);
Метод unref() — полезная специфика Node: таймер продолжит работать, но перестанет удерживать процесс от завершения. Классическая причина «скрипт отработал, а процесс не выходит» — как раз забытый живой интервал. Второй по частоте виновник — незакрытый HTTP-сервер.
Промис-версии таймеров живут в отдельном модуле и часто удобнее:
import { setTimeout as sleep } from 'node:timers/promises';
await sleep(1000);
Что было на этой странице в 2018 году
Первая версия вышла 20 апреля 2018 года и начиналась так:
«Два важнейших объекта в Node —
globalиprocess.»
Утверждение верное — настолько, что за прошедшие годы process дорос до размера, при котором разбирать его в одной статье с глобальными объектами стало неудобно. Поэтому здесь мы говорим про контейнер, а process вынесен в отдельный разбор.
Главное объяснение той версии не устарело ни на слово:
«В браузере переменная, объявленная на верхнем уровне, объявляется глобально. В Node дело обстоит иначе. Переменная, объявленная в модуле, не обладает глобальной доступностью; она ограничивается модулем.»
Это по-прежнему первое, что нужно понять про Node: модуль — не глобальная область. Объявили const base = 2 в двух разных файлах — конфликта не будет, это две разные переменные. В браузере 2018 года два скрипта с одинаковым var затирали друг друга, и разница действительно требовала объяснения.
Что изменилось с той версии: примеры были на var и require, globalThis в статье не упоминался (он появился в Node 12, годом позже), а fetch, structuredClone и AbortController глобально ещё не существовали — за каждым ходили в npm. Собственно, весь раздел «Что уже есть без импорта» выше — это список того, чего в 2018-м на этой странице просто не могло быть.
Buffer: почему не обычный массив
Buffer — самый «нодовый» из глобальных объектов: в браузере его нет. Он появился раньше, чем в JavaScript завезли типизированные массивы, и остался как основной способ работы с байтами.
const buf = Buffer.from('привет', 'utf8');
console.log(buf.length); // 12 — байты, а не символы
console.log(buf.toString('hex')); // d0bfd180d0b8d0b2d0b5d182
Формально Buffer — наследник Uint8Array, поэтому всё, что принимает типизированный массив, примет и его. Обратное неверно: методы вроде toString('base64') есть только у Buffer.
Одна ловушка, о которой стоит знать: Buffer.allocUnsafe(n) быстрее Buffer.alloc(n), потому что не обнуляет память — в выделенном куске лежат остатки предыдущих данных процесса. Если такой буфер отдать наружу не заполнив целиком, вы отправите клиенту случайные фрагменты чужой памяти. Пока не упрётесь в производительность, берите Buffer.alloc.
Про __dirname
В ESM-модулях __dirname и __filename не существуют — они были частью CommonJS. Замена штатная: import.meta.dirname и import.meta.filename (доступны с Node 21.2). Подробнее — в статье про модуль fs.
Смежные темы: объект process — самый большой из глобальных; промисы — где пригодится AbortController; модуль fs — где чаще всего встречается Buffer. Полный список — в справочнике по Node.js.
Частые вопросы
`global` устарел?
globalThis — стандарт и работает везде. Новый код пишите на нём.Почему в Node нет `window` и `document`?
`fetch` в Node — тот же самый, что в браузере?
Стоит ли использовать `process.nextTick`?
queueMicrotask или setImmediate.