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:
Подробнее о том, что добавляет хост-среда поверх языка, читайте в разделе среда браузера и спецификации.
Что хранится в глобальном объекте
Глобальный объект содержит два вида элементов:
- Встроенные возможности языка, определённые спецификацией ECMAScript и присутствующие везде:
Object,Array,Function,String,Number,Boolean,Symbol,BigInt,Math,JSON,Date,RegExp,Promise,Map,Set, конструкторы ошибок, а также глобальные функции:parseInt,isNaNиeval. - Хост-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выбрасывать исключение вместо того, чтобы молча создавать глобальную переменную.
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 и модули, чтобы не загрязнять его, и включайте строгий режим для раннего обнаружения случайных глобальных переменных.