> В чем отличие среды исполнения JavaScript в браузере и на сервере (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: YADRO

Стек: JavaScript

> Пример ответа

Короткий ответ

Основное отличие - в окружении и доступных API. В браузере есть DOM, BOM, Web APIs (fetch, setTimeout, event loop с макрозадачами и микрозадачами), а на сервере (Node.js) - модули fs, http, process, работа с файловой системой и отсутствие DOM. Event loop в Node.js дополнен фазами (timers, I/O callbacks, poll, check, close). Также различаются глобальные объекты: window vs globalThis.

Подробное объяснение

Среда исполнения JavaScript в браузере и на сервере (Node.js) имеет принципиальные различия, обусловленные разными задачами: браузер - это клиент для взаимодействия с пользователем, сервер - для обработки запросов и работы с ресурсами.

Глобальные объекты и API:

  • Браузер: window (или globalThis), document, navigator, location, history, localStorage, fetch, WebSocket, setTimeout, requestAnimationFrame.
  • Node.js: global (или globalThis), process, Buffer, require, module.exports, __dirname, __filename, fs, http, path, crypto.

Модульная система:

  • Браузер: ES modules (import/export) через <script type="module">, CommonJS не поддерживается нативно.
  • Node.js: поддерживает и CommonJS (require/module.exports), и ES modules (с .mjs или "type": "module" в package.json).

Event loop:

  • Браузер: имеет макрозадачи (setTimeout, setInterval, UI rendering) и микрозадачи (Promise, MutationObserver). Очередь микрозадач обрабатывается после каждой макрозадачи.
  • Node.js: event loop разделён на фазы: timers, pending callbacks, idle/prepare, poll, check, close callbacks. Микрозадачи обрабатываются между фазами. Также есть process.nextTick - особая очередь, выполняемая перед микрозадачами.

Потоки и параллелизм:

  • Браузер: однопоточный, но использует Web Workers для фоновых задач (без доступа к DOM).
  • Node.js: однопоточный для JS, но использует libuv thread pool для I/O операций (fs, crypto), а также Worker Threads для CPU-интенсивных задач.

Безопасность:

  • Браузер: изолированная среда (same-origin policy, CORS, Content Security Policy), нет доступа к файловой системе.
  • Node.js: полный доступ к файловой системе, сети, процессам, что требует осторожности с уязвимостями (path traversal, command injection).

Разное:

  • Браузер: поддерживает fetch и XMLHttpRequest для HTTP-запросов, WebSocket для реального времени.
  • Node.js: http/https модули для создания серверов и клиентов, socket.io для WebSocket.

На практике

Для frontend-разработчика важно понимать, что код, написанный для браузера, не всегда работает на сервере и наоборот. Например, использование document.querySelector вызовет ошибку в Node.js. При разработке изоморфных (универсальных) приложений (например, Next.js, Nuxt.js) нужно проверять окружение через typeof window !== 'undefined' или использовать globalThis.

Типичные сценарии:

  • Серверный рендеринг (SSR): код выполняется на сервере, где нет DOM, поэтому нужно избегать прямых обращений к window или document.
  • API-маршруты в Next.js: выполняются в Node.js, поэтому можно использовать fs для чтения файлов, но нельзя использовать localStorage.
  • Middleware: в браузере нет process.env, но можно использовать import.meta.env (Vite) или переменные окружения через Webpack DefinePlugin.

Инструменты для универсального кода:

  • globalThis - стандартный способ получить глобальный объект в любом окружении.
  • Полифиллы (например, whatwg-fetch для Node.js).
  • Условные импорты через динамический import() или проверку typeof.

Пример кода

JAVASCRIPT
// Проверка окружения
if (typeof window !== 'undefined' && window.document) {
console.log('Running in browser');
document.getElementById('app').innerHTML = 'Hello from browser';
} else {
console.log('Running in Node.js');
const fs = require('fs');
fs.writeFileSync('log.txt', 'Hello from server');
}
// Использование globalThis
globalThis.myGlobal = 'accessible everywhere';
console.log(globalThis.myGlobal); // работает и в браузере, и в Node.js
// Event loop разница
// Браузер: setTimeout и Promise
setTimeout(() => console.log('macro'), 0);
Promise.resolve().then(() => console.log('micro'));
// Вывод: micro, macro
// Node.js: process.nextTick
process.nextTick(() => console.log('nextTick'));
setTimeout(() => console.log('timer'), 0);
Promise.resolve().then(() => console.log('promise'));
// Вывод: nextTick, promise, timer

Как отвечать на собеседовании

Начни с ключевого отличия - окружение и доступные API. Затем перечисли основные различия: глобальные объекты, модульная система, event loop, безопасность. Для senior-уровня важно показать понимание event loop в Node.js (фазы, process.nextTick) и как это влияет на порядок выполнения кода. Приведи пример изоморфного кода и объясни, как избежать ошибок при SSR. Упомяни про globalThis как универсальный глобальный объект. Если спросят про производительность, скажи, что Node.js эффективнее для I/O, а браузер - для UI.

Что проверяет интервьюер

  • Глубину понимания сред исполнения, а не просто заученные факты.
  • Умение проектировать изоморфный код и избегать платформенных зависимостей.
  • Знание event loop в деталях (фазы, микрозадачи, process.nextTick).
  • Понимание безопасности и ограничений каждой среды.
  • Способность объяснить trade-offs при выборе между браузерным и серверным кодом.

Типичные ошибки

  • Утверждение, что Node.js - это фреймворк, а не среда исполнения.
  • Игнорирование process.nextTick и его приоритета.
  • Путаница между window и global (в Node.js нет window).
  • Предположение, что fetch доступен в Node.js по умолчанию (до версии 18 требовался полифилл).
  • Использование require в браузере без сборщика (Webpack, Vite).
  • Непонимание, что setTimeout(fn, 0) в Node.js не гарантирует выполнение сразу после микрозадач из-за фаз event loop.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью