← JavaScript/Глобальный объект#127 из 383← ПредыдущийСледующий →+15 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

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

Вы пишете npm-библиотеку, которая должна работать одновременно в браузере и Node.js. В каком-то месте нужно добавить полифил, если браузер его не поддерживает. Для этого нужно знать, где найти глобальный контекст и как безопасно проверить наличие API.

Что решает эта тема

JavaScript выполняется в разных средах, и у каждой — свой глобальный объект. Раньше код приходилось писать по-разному для браузера и Node.js. globalThis — стандартный способ обратиться к глобальному объекту в любой среде.

На основе предыдущих уроков

  • переменные: разница между var, let, const и глобальной областью
  • модули: почему модульный код лучше глобального состояния
  • объекты: глобальный объект — это просто объект
  • window, global и globalThis

    | Среда | Глобальный объект |

    |-------|-------------------|

    | Браузер | window |

    | Node.js | global |

    | Web Worker | self |

    | Универсально | globalThis |

    // globalThis работает везде — в браузере, Node.js, Web Workers
    console.log(typeof globalThis)  // 'object' — всегда
    
    // В браузере: globalThis === window  → true
    // В Node.js:  globalThis === global  → true

    var на верхнем уровне → свойство globalThis

    var appName = 'MyShop'
    console.log(globalThis.appName)  // 'MyShop' (в браузере: window.appName)
    
    // let и const — НЕ попадают в globalThis
    let version = '2.0'
    console.log(globalThis.version)  // undefined

    Это плохая практика: переменная с именем name или length случайно перезапишет встроенное свойство window.

    Безопасная проверка через typeof

    // НЕПРАВИЛЬНО — в Node.js выбросит ReferenceError:
    if (window !== undefined) { ... }
    
    // ПРАВИЛЬНО — typeof не бросает ошибку для необъявленных переменных:
    if (typeof window !== 'undefined') {
      // Мы в браузере
      console.log('Браузер, начинаем рендеринг')
    } else {
      // Мы в Node.js или другой среде
      console.log('Серверная среда, SSR режим')
    }

    Или используйте globalThis — проверяйте через него без риска:

    const isBrowser = typeof globalThis.document !== 'undefined'
    const isNode    = typeof globalThis.process  !== 'undefined'

    Полифилы через globalThis

    Полифил добавляет новый API в старые среды через глобальный объект:

    // Добавляем структурированный полифил для structuredClone
    if (typeof globalThis.structuredClone === 'undefined') {
      globalThis.structuredClone = function(obj) {
        return JSON.parse(JSON.stringify(obj))  // упрощённый вариант
      }
    }
    
    // Теперь structuredClone работает везде
    const copy = structuredClone({ a: 1, b: [2, 3] })

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

    Ошибка 1: прямое обращение к window в Node.js

    // Сломано в Node.js — ReferenceError: window is not defined
    if (window.innerWidth < 768) {
      loadMobileLayout()
    }
    
    // Исправлено:
    if (typeof globalThis.window !== 'undefined' && window.innerWidth < 768) {
      loadMobileLayout()
    }

    Ошибка 2: глобальная переменная вместо модуля

    // Плохо: любой код может случайно изменить состояние
    var currentUser = null
    
    // Хорошо: модульное состояние — только один файл управляет им
    // user-store.js
    let _currentUser = null
    
    export const setUser = (user) => { _currentUser = user }
    export const getUser = () => _currentUser

    Ошибка 3: feature detection по строке User-Agent вместо проверки свойства

    // Плохо: ненадёжно, легко сломать
    const isChrome = navigator.userAgent.includes('Chrome')
    
    // Хорошо: проверяем наличие конкретного API
    const supportsWebP = typeof globalThis.createImageBitmap !== 'undefined'
    const supportsStorage = typeof globalThis.localStorage !== 'undefined'

    В реальных проектах

  • Универсальные библиотеки (isomorphic): globalThis позволяет одному коду работать на сервере (Next.js SSR) и в браузере без if/else на каждый чих
  • Полифилы: core-js и подобные библиотеки добавляют API в globalThis для старых браузеров
  • Тесты: в Jest globalThis — это jsdom-окружение; через него мокируют fetch и другие API
  • Конфигурация: globalThis.APP_CONFIG = {...} — антипаттерн; лучше использовать переменные окружения через process.env или import.meta.env
  • Примеры

    Feature detection, полифил и безопасная проверка среды через globalThis

    // 1. globalThis — универсальный доступ к глобальному объекту
    console.log(typeof globalThis)  // 'object'
    console.log(typeof globalThis.Array)   // 'function' — встроенные всегда есть
    console.log(typeof globalThis.Promise) // 'function'
    
    // 2. Feature detection — проверяем конкретный API, не среду
    function checkEnvironmentFeatures() {
      const checks = {
        fetch:               typeof globalThis.fetch               !== 'undefined',
        Promise:             typeof globalThis.Promise             !== 'undefined',
        structuredClone:     typeof globalThis.structuredClone     !== 'undefined',
        WebSocket:           typeof globalThis.WebSocket           !== 'undefined',
        localStorage:        typeof globalThis.localStorage        !== 'undefined',
        IntersectionObserver:typeof globalThis.IntersectionObserver!== 'undefined',
      }
    
      console.log('=== Поддержка API ===')
      for (const [api, supported] of Object.entries(checks)) {
        const mark = supported ? 'есть' : 'нет'
        console.log(`  ${api.padEnd(22)}: ${mark}`)
      }
      return checks
    }
    
    checkEnvironmentFeatures()
    
    // 3. Полифил через globalThis — добавляем если нет
    if (typeof globalThis.structuredClone === 'undefined') {
      globalThis.structuredClone = function deepClone(obj) {
        if (obj === null || typeof obj !== 'object') return obj
        return JSON.parse(JSON.stringify(obj))
      }
      console.log('\nПолифил structuredClone установлен')
    } else {
      console.log('\nstructuredClone уже доступен нативно')
    }
    
    const original = { name: 'Алиса', scores: [95, 87, 92] }
    const clone = structuredClone(original)
    clone.scores.push(100)
    
    console.log('Оригинал scores:', original.scores) // [95, 87, 92]
    console.log('Клон scores:    ', clone.scores)    // [95, 87, 92, 100]
    
    // 4. Определение среды — безопасно через typeof
    const env = {
      isBrowser: typeof globalThis.document  !== 'undefined',
      isNode:    typeof globalThis.process   !== 'undefined' && !!globalThis.process.versions?.node,
      isWorker:  typeof globalThis.WorkerGlobalScope !== 'undefined',
    }
    
    console.log('\n=== Среда выполнения ===')
    console.log('Браузер:', env.isBrowser)
    console.log('Node.js:', env.isNode)
    console.log('Worker: ', env.isWorker)

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

    Вы пишете npm-библиотеку, которая должна работать одновременно в браузере и Node.js. В каком-то месте нужно добавить полифил, если браузер его не поддерживает. Для этого нужно знать, где найти глобальный контекст и как безопасно проверить наличие API.

    Что решает эта тема

    JavaScript выполняется в разных средах, и у каждой — свой глобальный объект. Раньше код приходилось писать по-разному для браузера и Node.js. globalThis — стандартный способ обратиться к глобальному объекту в любой среде.

    На основе предыдущих уроков

  • переменные: разница между var, let, const и глобальной областью
  • модули: почему модульный код лучше глобального состояния
  • объекты: глобальный объект — это просто объект
  • window, global и globalThis

    | Среда | Глобальный объект |

    |-------|-------------------|

    | Браузер | window |

    | Node.js | global |

    | Web Worker | self |

    | Универсально | globalThis |

    // globalThis работает везде — в браузере, Node.js, Web Workers
    console.log(typeof globalThis)  // 'object' — всегда
    
    // В браузере: globalThis === window  → true
    // В Node.js:  globalThis === global  → true

    var на верхнем уровне → свойство globalThis

    var appName = 'MyShop'
    console.log(globalThis.appName)  // 'MyShop' (в браузере: window.appName)
    
    // let и const — НЕ попадают в globalThis
    let version = '2.0'
    console.log(globalThis.version)  // undefined

    Это плохая практика: переменная с именем name или length случайно перезапишет встроенное свойство window.

    Безопасная проверка через typeof

    // НЕПРАВИЛЬНО — в Node.js выбросит ReferenceError:
    if (window !== undefined) { ... }
    
    // ПРАВИЛЬНО — typeof не бросает ошибку для необъявленных переменных:
    if (typeof window !== 'undefined') {
      // Мы в браузере
      console.log('Браузер, начинаем рендеринг')
    } else {
      // Мы в Node.js или другой среде
      console.log('Серверная среда, SSR режим')
    }

    Или используйте globalThis — проверяйте через него без риска:

    const isBrowser = typeof globalThis.document !== 'undefined'
    const isNode    = typeof globalThis.process  !== 'undefined'

    Полифилы через globalThis

    Полифил добавляет новый API в старые среды через глобальный объект:

    // Добавляем структурированный полифил для structuredClone
    if (typeof globalThis.structuredClone === 'undefined') {
      globalThis.structuredClone = function(obj) {
        return JSON.parse(JSON.stringify(obj))  // упрощённый вариант
      }
    }
    
    // Теперь structuredClone работает везде
    const copy = structuredClone({ a: 1, b: [2, 3] })

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

    Ошибка 1: прямое обращение к window в Node.js

    // Сломано в Node.js — ReferenceError: window is not defined
    if (window.innerWidth < 768) {
      loadMobileLayout()
    }
    
    // Исправлено:
    if (typeof globalThis.window !== 'undefined' && window.innerWidth < 768) {
      loadMobileLayout()
    }

    Ошибка 2: глобальная переменная вместо модуля

    // Плохо: любой код может случайно изменить состояние
    var currentUser = null
    
    // Хорошо: модульное состояние — только один файл управляет им
    // user-store.js
    let _currentUser = null
    
    export const setUser = (user) => { _currentUser = user }
    export const getUser = () => _currentUser

    Ошибка 3: feature detection по строке User-Agent вместо проверки свойства

    // Плохо: ненадёжно, легко сломать
    const isChrome = navigator.userAgent.includes('Chrome')
    
    // Хорошо: проверяем наличие конкретного API
    const supportsWebP = typeof globalThis.createImageBitmap !== 'undefined'
    const supportsStorage = typeof globalThis.localStorage !== 'undefined'

    В реальных проектах

  • Универсальные библиотеки (isomorphic): globalThis позволяет одному коду работать на сервере (Next.js SSR) и в браузере без if/else на каждый чих
  • Полифилы: core-js и подобные библиотеки добавляют API в globalThis для старых браузеров
  • Тесты: в Jest globalThis — это jsdom-окружение; через него мокируют fetch и другие API
  • Конфигурация: globalThis.APP_CONFIG = {...} — антипаттерн; лучше использовать переменные окружения через process.env или import.meta.env
  • Примеры

    Feature detection, полифил и безопасная проверка среды через globalThis

    // 1. globalThis — универсальный доступ к глобальному объекту
    console.log(typeof globalThis)  // 'object'
    console.log(typeof globalThis.Array)   // 'function' — встроенные всегда есть
    console.log(typeof globalThis.Promise) // 'function'
    
    // 2. Feature detection — проверяем конкретный API, не среду
    function checkEnvironmentFeatures() {
      const checks = {
        fetch:               typeof globalThis.fetch               !== 'undefined',
        Promise:             typeof globalThis.Promise             !== 'undefined',
        structuredClone:     typeof globalThis.structuredClone     !== 'undefined',
        WebSocket:           typeof globalThis.WebSocket           !== 'undefined',
        localStorage:        typeof globalThis.localStorage        !== 'undefined',
        IntersectionObserver:typeof globalThis.IntersectionObserver!== 'undefined',
      }
    
      console.log('=== Поддержка API ===')
      for (const [api, supported] of Object.entries(checks)) {
        const mark = supported ? 'есть' : 'нет'
        console.log(`  ${api.padEnd(22)}: ${mark}`)
      }
      return checks
    }
    
    checkEnvironmentFeatures()
    
    // 3. Полифил через globalThis — добавляем если нет
    if (typeof globalThis.structuredClone === 'undefined') {
      globalThis.structuredClone = function deepClone(obj) {
        if (obj === null || typeof obj !== 'object') return obj
        return JSON.parse(JSON.stringify(obj))
      }
      console.log('\nПолифил structuredClone установлен')
    } else {
      console.log('\nstructuredClone уже доступен нативно')
    }
    
    const original = { name: 'Алиса', scores: [95, 87, 92] }
    const clone = structuredClone(original)
    clone.scores.push(100)
    
    console.log('Оригинал scores:', original.scores) // [95, 87, 92]
    console.log('Клон scores:    ', clone.scores)    // [95, 87, 92, 100]
    
    // 4. Определение среды — безопасно через typeof
    const env = {
      isBrowser: typeof globalThis.document  !== 'undefined',
      isNode:    typeof globalThis.process   !== 'undefined' && !!globalThis.process.versions?.node,
      isWorker:  typeof globalThis.WorkerGlobalScope !== 'undefined',
    }
    
    console.log('\n=== Среда выполнения ===')
    console.log('Браузер:', env.isBrowser)
    console.log('Node.js:', env.isNode)
    console.log('Worker: ', env.isWorker)

    Задание

    Напиши функцию isSupported(featureName), которая проверяет, существует ли в глобальном окружении свойство с таким именем. Функция должна возвращать true или false, не бросая ошибок.

    Подсказка

    return typeof globalThis[featureName] !== 'undefined'

    Загружаем среду выполнения...
    Загружаем AI-помощника...