- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
全局对象是 JavaScript 运行时提供的“公共舞台”,其中存放着语言内建对象(如Array、Promise)以及环境特有的值(如浏览器中的window.innerHeight)。本篇指南以《现代 JavaScript 教程》中“全局对象”一章为骨架,结合本仓库 1-js/13-modules/01-modules-intro/article.md(模块机制)、1-js/03-code-quality/06-polyfills/article.md(polyfill 与转译器)以及 1-js/06-advanced-functions/04-var/article.md(老旧的var)等章节的源码级证据,帮你彻底理解:全局对象有哪些名字、var与let声明在全局作用域上的天壤之别、如何安全地向全局对象写入共享数据,以及如何借助全局对象编写 polyfill 检测旧环境。
什么是全局对象
全局对象(global object)提供可在任何地方使用的变量和函数。默认情况下,这些全局变量内建于语言或环境中,无论代码写在哪个作用域,都可以直接访问它们。
不同 JavaScript 运行环境对全局对象有不同的命名:
| 环境 | 全局对象名称 |
|---|---|
| 浏览器 | window |
| Node.js | global |
| 其他环境 | 可能有别的名字 |
最近,globalThis被作为全局对象的标准名称加入到了 JavaScript 中,所有环境都应该支持该名称,所有主流浏览器都已支持它。
因此在实际编码中有一个实用建议:如果你的脚本只在浏览器中运行,可以放心使用window;但如果脚本可能运行在其他环境(Node.js、Web Worker 等),则最好使用globalThis,以保证代码跨环境的可移植性。
全局对象的属性可以被直接访问
全局对象的所有属性都可以被直接访问,也就是说window上的属性等同于全局作用域中的变量:
alert("Hello"); // 等同于 window.alert("Hello");反过来,在全局作用域中用var声明的变量,也会自动挂载到全局对象上,从而获得“处处可见”的能力。
var声明的全局变量会成为全局对象的属性
在浏览器中,使用var(而不是let/const!)声明的全局函数和变量,会成为全局对象的属性:
var gVar = 5; alert(window.gVar); // 5(成为了全局对象的属性)函数声明(特指在主代码流中具有function关键字的语句,而不是函数表达式)也有同样的效果。
但请不要依赖这种行为!它只是出于历史兼容性而保留的旧机制。现代脚本使用 JavaScript 模块(import/export),不会产生这种“污染全局对象”的效果。
如果我们使用let声明,就不会发生这种情况:
let gLet = 5; alert(window.gLet); // undefined(不会成为全局对象的属性)为什么会这样?从源码角度看,这是
var与let在声明语义上的本质区别。本教程 老旧的 "var" 一章详细讲解了var没有块级作用域、变量提升等“远古怪兽”特性——正是这种“函数作用域或全局作用域”的声明规则,使得顶层var声明会与全局对象绑定;而let/const采用块级词法作用域,不会写入全局对象。
如何显式地向全局对象写入共享值
如果一个值非常重要,以至于你想使它在全局范围内可用,可以直接将其作为属性写入全局对象:
// 将当前用户信息全局化,以允许所有脚本访问它 window.currentUser = { name: "John" }; // 代码中的另一个位置 alert(currentUser.name); // John // 或者,如果我们有一个名为 "currentUser" 的局部变量 // 从 window 显式地获取它(这是安全的!) alert(window.currentUser.name); // John这里蕴含着一个重要的最佳实践:当局部作用域中存在同名变量时,通过window.x显式访问全局对象属性是安全的,可以避免名字遮蔽(shadowing)带来的歧义。
不过,教程同时也给出了严肃的忠告:一般不建议使用全局变量,全局变量应尽可能的少。与使用外部变量或全局变量相比,函数获取“输入”变量并产生特定“输出”的代码设计更加清晰、不易出错且更易于测试。把共享数据显式挂到全局对象上,应仅限于“项目级确实需要全局共享”的少量场景。
模块环境下的全局变量真相
在浏览器中,每个<script type="module">都有独立的顶级作用域,模块之间看不到彼此的顶层变量。本仓库的示例 scopes.view/user.js 中let user = "John";声明了一个模块级变量,另一个脚本试图直接访问它会报错;而正确做法是像 scopes-working.view/user.js 那样用export let user = "John";导出,再由 scopes-working.view/hello.js 通过import {user} from './user.js'导入使用。
也就是说,对于模块,我们应该使用导入/导出交换数据,而不是依赖全局变量。但如果你确实需要创建“窗口级别”的全局变量,可以显式地赋值给window的一个属性,例如window.user = "John"——这样所有脚本(无论是否带有type="module")都能看到它。需要注意的是,模块章节同样强调:创建这种全局变量并不是一个好的方式,请尽量避免。
使用全局对象检测环境并编写 polyfill
我们使用全局对象来测试对现代语言功能的支持。例如,测试环境中是否存在内建的Promise对象(在版本特别旧的浏览器中不存在):
if (!window.Promise) { alert("Your browser is really old!"); }如果没有(例如我们使用的是旧版浏览器),那么就可以创建 “polyfills”——添加环境不支持但在现代标准中存在的功能:
if (!window.Promise) { window.Promise = ... // 定制实现现代语言功能 }polyfill 的底层原理:动态语言的可修补性
polyfill 之所以可行,是因为JavaScript 是一种高度动态的语言,脚本可以添加/修改任何函数,甚至包括内建函数。这一点在本教程 Polyfill 和转译器 一章有详尽的展开。
polyfill 的典型写法是“先检测、后修补”。比如Math.trunc(n)是一个“截断”数字小数部分的函数,Math.trunc(1.23)返回1,但一些非常过时的 JavaScript 引擎中并没有这个函数,此时可以这样实现 polyfill:
if (!Math.trunc) { // 如果没有这个函数 // 实现它 Math.trunc = function(number) { // Math.ceil 和 Math.floor 甚至存在于上古年代的 JavaScript 引擎中 return number < 0 ? Math.ceil(number) : Math.floor(number); }; }同理,现代语法层面的新特性(如空值合并运算符??)无法靠 polyfill 解决,需要借助转译器(Transpiler)处理——例如 Babel 会把height = height ?? 100;重写为height = (height !== undefined && height !== null) ? height : 100;。这就是“新语法用转译器、新内建函数用 polyfill”的分工原则:
- 转译器:解析现代代码并改写为旧引擎能理解的语法结构;
- polyfill:直接向全局对象(或内建对象)补充缺失的函数实现。
在实际项目中,两个著名的 polyfill 库包括 core-js(支持大量特性、允许按需引入)等;而 webpack 等现代构建系统提供了在每次代码变更时自动运行转译器的方法,可无缝集成到开发流程中。
总结
- 全局对象包含应该在任何位置都可见的变量,其中包括 JavaScript 的内建方法(如
Array)和环境特有的值(如浏览器中的window.innerHeight—— 窗口高度)。 - 全局对象有一个通用名称
globalThis;但更常见的是使用“老式”的环境特定名称,例如window(浏览器)和global(Node.js)。 - 仅当值对于项目而言确实是全局的时候,才应将其存储在全局对象中,并且要保持数量最少。
- 在浏览器中,除非我们使用 模块,否则使用
var声明的全局函数和变量会成为全局对象的属性;let/const则不会。 - 为了使代码面向未来并更易于理解,我们应该使用直接的方式访问全局对象的属性,如
window.x。
一句话实践准则:跨环境代码优先使用globalThis;确实需要全局共享的数据用window.x = ...显式写入并尽量少用;而检测旧环境、补齐缺失内建能力(polyfill)时,全局对象就是你最可靠的“侦察与修补”入口。
- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
相关推荐
深入理解 JavaScript 全局对象:window、globalThis、全局变量与 Polyfill 实战(Modern JavaScript Tutorial)
深入理解 JavaScript 全局对象:window、globalThis、全局变量与 Polyfill 实战(Modern JavaScript Tutor
文档/教程前端Brython 与 JavaScript 互操作完全指南:从 `window` 对象到 `__BRYTHON__` 双向桥接
Brython 与 JavaScript 互操作完全指南:从 window 对象到 __BRYTHON__ 双向桥接 本文以 Brython(Browser P
编程语言语言运行时编译器前端深入解析 freeCodeCamp JavaScript 全局作用域:Global Scope and Functions 挑战
深入解析 freeCodeCamp JavaScript 全局作用域:Global Scope and Functions 挑战 本篇文章以 freeCodeC
前端后端教育
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考