W3docs

JavaScript globalThis и глобальный объект

Изучите глобальный объект JavaScript: globalThis, window, global, встроенные возможности, утечки var и функций, а также способы избежать загрязнения глобального пространства имён.

Каждая программа на JavaScript выполняется внутри одного объекта верхнего уровня, который существует ещё до начала выполнения вашего кода: глобального объекта. В нём хранятся встроенные возможности языка (Array, Math, JSON, setTimeout и так далее), и именно в него неявно попадает всё, что вы объявляете на самом верхнем уровне обычного скрипта. На этой странице объясняется, что такое глобальный объект, как получить к нему доступ из любого окружения с помощью globalThis, что именно в нём хранится, почему некоторые объявления верхнего уровня «утекают» в него, а другие нет, и как держать собственные глобальные переменные под контролем.

Что такое глобальный объект

Глобальный объект является корнем цепочки областей видимости. Когда вы обращаетесь к имени, которое не найдено ни в одной из вложенных функций или блоков, движок в конечном счёте ищет его как свойство глобального объекта. Именно поэтому Math.max или JSON.parse работают в любом месте — они являются свойствами глобального объекта, которые среда выполнения настраивает за вас.

Каждая среда выполнения предоставляет этот объект под разным именем, что исторически создавало неудобства при написании кода для разных окружений:

  • Браузеры называют его window (а также предоставляют self и frames).
  • Web Workers называют его self (в воркере нет window).
  • Node.js называет его global.

globalThis: переносимый глобальный объект

Поскольку имя объекта различается в зависимости от окружения, в ES2020 был добавлен globalThis — единственная стандартная ссылка, которая указывает на глобальный объект везде: в браузерах, воркерах, Node.js, Deno и не только. Используйте его всякий раз, когда вам нужен настоящий глобальный объект в коде, предназначенном для работы в нескольких средах.

console.log(typeof globalThis); // "object" in every environment

// Each of these is true only in its own environment:
// globalThis === window  -> true in a browser tab
// globalThis === self    -> true in a browser or a Web Worker
// globalThis === global  -> true in Node.js

Попытка использовать неправильное имя приводит к ReferenceError — именно эту проблему и решает globalThis:

javascript— editable

Подробнее о том, что добавляет хост-среда поверх языка, читайте в разделе среда браузера и спецификации.

Что хранится в глобальном объекте

Глобальный объект содержит два вида элементов:

  1. Встроенные возможности языка, определённые спецификацией ECMAScript и присутствующие везде: Object, Array, Function, String, Number, Boolean, Symbol, BigInt, Math, JSON, Date, RegExp, Promise, Map, Set, конструкторы ошибок, а также глобальные функции: parseInt, isNaN и eval.
  2. Хост-API (среды выполнения), добавляемые рантаймом: document, fetch, localStorage, setTimeout и alert в браузере; process, Buffer и require в Node.js.
// Built-ins are reachable through the global object:
console.log(globalThis.Math.max(2, 7, 4)); // 7
console.log(globalThis.JSON.stringify({ ok: true })); // {"ok":true}

Почему var и функции верхнего уровня «утекают» в глобальный объект

В обычном (не модульном) браузерном скрипте переменная var, объявленная на верхнем уровне, и объявление функции верхнего уровня становятся свойствами window. Это устаревшее поведение, закреплённое в языке из соображений обратной совместимости:

// In a classic browser script (not a module):
var greeting = 'hi';
function greet() { return greeting; }

console.log(window.greeting);     // "hi"
console.log(typeof window.greet); // "function"

Объявления с блочной областью видимости ведут себя иначе. let, const и class на верхнем уровне создают глобальные привязки, но не добавляются в глобальный объект:

let count = 1;
const name = 'app';

console.log(window.count); // undefined
console.log(window.name);  // "" — note: window.name is a pre-existing browser property, not your variable

Два важных уточнения:

  • ES-модули не «утекают» вообще. Объявления var и function верхнего уровня внутри <script type="module"> (или любого файла с import/export) находятся в области видимости модуля, поэтому ничто — даже var — не добавляется в глобальный объект.
  • Файлы Node.js тоже являются модулями. Переменная var верхнего уровня в файле Node.js .js ограничена областью видимости этого модуля и не добавляется в global, как это происходит в обычном браузерном скрипте.

Это различие является одной из практических причин, по которым современный код предпочитает let/const и модули. Подробнее о механизмах поднятия и областей видимости var читайте в разделах устаревший «var» и область видимости переменных и замыкание.

Как избежать загрязнения глобального пространства имён

Глобальные переменные — это разделяемое, изменяемое состояние: любой скрипт может их перезаписать, а конфликты имён порождают ошибки, которые трудно отследить. Несколько правил помогут сохранить пространство имён чистым:

  • Отдавайте предпочтение let/const и модулям. Они полностью исключают объявления из глобального объекта.
  • Используйте единое пространство имён, если вам действительно нужна глобальная переменная: так вы добавляете одно свойство вместо множества.
  • Включайте строгий режим. "use strict" заставляет необъявленное присваивание вроде x = 5 выбрасывать исключение вместо того, чтобы молча создавать глобальную переменную.
javascript— editable

ES-модули сохраняют глобальное пространство имён чистым

ES-модули разделяют код на переиспользуемые файлы, объявления верхнего уровня в которых остаются приватными для каждого файла, если явно не экспортированы. Это современная альтернатива добавлению значений в глобальный объект:

// file: math.js
export const add = (a, b) => a + b;

// file: app.js
import { add } from './math.js';
console.log(add(2, 3)); // 5
// `add` is imported, not read from a global — nothing leaks onto window/global.

Заключение

Глобальный объект — это корневая область видимости среды выполнения: в нём хранятся встроенные возможности языка и хост-API, а в обычных скриптах туда также попадают объявления var и function верхнего уровня. Используйте globalThis для переносимого доступа к нему, полагайтесь на let/const и модули, чтобы не загрязнять его, и включайте строгий режим для раннего обнаружения случайных глобальных переменных.

Практика

Практика
Какие из перечисленных подходов являются лучшими практиками при работе с глобальным объектом в JavaScript?
Какие из перечисленных подходов являются лучшими практиками при работе с глобальным объектом в JavaScript?
Was this page helpful?