Откуда берутся идентификаторы __dirname и __filename, require и import, fetch и Array?nodejs-33

1. CommonJS-специфичные переменные

__dirname и __filename

console.log(__dirname);  // /path/to/current/directory
console.log(__filename); // /path/to/current/file.js

Происхождение:

  • Автоматически инжектируются Node.js для каждого CommonJS модуля
  • Формируются при оборачивании модуля в функцию:
(function(exports, require, module, __filename, __dirname) {
  // Ваш модуль
});
  • Важно: Недоступны в ESM модулях (используйте import.meta.url)

require

const fs = require('fs');

Происхождение:

  • Часть системы модулей CommonJS
  • Инжектируется аналогично __dirname
  • Реализован в Node.js через внутренний метод Module._load

2. ECMAScript Modules специфика

import/export

import fs from 'fs';
export function foo() {}

Происхождение:

  • Спецификация ECMAScript (ES6+)
  • Реализованы в Node.js через:
    • Анализатор синтаксиса (академический)
    • Линковщик модулей (спецификация ESM)
  • В браузерах - через нативную реализацию

3. Web API и глобальные объекты

fetch

fetch('https://api.example.com');

Происхождение:

  • Часть Web API (WHATWG Fetch Standard)
  • В Node.js:
    • До версии 18 - через полифилы (node-fetch)
    • Начиная с Node.js 18 - нативная реализация (экспериментальная)
    • Полностью стабилен с Node.js 21

Array

const arr = new Array(1, 2, 3);

Происхождение:

  • Встроенный глобальный объект JavaScript (ECMAScript)
  • Часть спецификации языка
  • Реализован в движке V8 (для Node.js)

Сравнительная таблица происхождения

Идентификатор Тип Источник Доступность
__dirnameПеременнаяNode.js CommonJS wrapperТолько CommonJS
__filenameПеременнаяNode.js CommonJS wrapperТолько CommonJS
requireФункцияNode.js модульная системаТолько CommonJS
import/exportСинтаксисECMAScript стандартESM модули
fetchФункцияWeb API / Node.js реализацияГлобально (новые версии)
ArrayКонструкторECMAScript ядроГлобально везде

Технические детали реализации

Как Node.js инжектирует CommonJS переменные

// При загрузке модуля Node.js делает примерно следующее:
const wrapper = [
  '(function(exports, require, module, __filename, __dirname) { ',
  '\n});'
];

const wrapped = wrapper[0] + moduleCode + wrapper[1];
const compiled = vm.runInThisContext(wrapped, {
  filename: modulePath,
  lineOffset: 0,
  displayErrors: true
});

compiled(module.exports, require, module, filename, dirname);

Полифил fetch в старых Node.js

// До Node.js 18 нужно было делать так:
const fetch = require('node-fetch');
// Или глобально:
globalThis.fetch = require('node-fetch');

Различия сред выполнения

  1. Браузер:

    • Нет __dirname, __filename, require
    • Есть fetch, Array, import/export
  2. Node.js:

    • Все перечисленные идентификаторы доступны
    • Но в разных модульных системах разные наборы
  3. Deno/Bun:

    • Современные альтернативы с другим набором глобалов

Резюмируем

  1. Node.js-специфичные:

    • __dirname, __filename, require - инжектятся оберткой CommonJS модуля
  2. Стандарт ECMAScript:

    • import/export - часть ES модулей
    • Array - базовый конструктор языка
  3. Web API:

    • fetch - перенесен из браузеров в Node.js
  4. Ключевые различия:

    • CommonJS vs ESM - разные наборы доступных идентификаторов
    • Версия Node.js критична для некоторых API (как fetch)
    • Глобальные объекты зависят от среды выполнения

Понимание происхождения этих идентификаторов помогает:

  • Избегать ошибок при переходе между средами
  • Правильно выбирать API для разных версий Node.js
  • Осознанно использовать модульные системы