全局作用域下var与let的差异:全局对象属性的实际影响
顶层作用域let/var与全局对象的差异解析
一、实际影响
避免全局对象污染
用var声明的顶层变量会成为全局对象(浏览器中是window,Node.js中是globalThis)的可枚举属性,容易造成命名冲突。比如:var title = "全局标题"; console.log(window.title); // 输出"全局标题"而
let声明的变量不会挂载到全局对象,不会给全局对象增加额外属性:let title = "全局标题"; console.log(window.title); // 输出undefined这在多人协作或引入第三方库时,能大幅减少因全局属性重名导致的意外bug。
访问方式的限制
你无法通过全局对象来访问let声明的顶层变量,比如想动态通过变量名访问时:var foo = 1; console.log(window["foo"]); // 输出1 let bar = 2; console.log(window["bar"]); // 输出undefined这既是限制也是保护——它避免了通过全局对象意外修改顶层变量的可能,让变量的访问更可控。
对全局对象原有属性的保护
如果全局对象本身已有某个属性(比如浏览器的window.name),用var声明同名变量会直接覆盖这个属性:var name = "张三"; console.log(window.name); // 输出"张三"(原有的window.name被覆盖)但
let声明的同名变量会在顶层词法环境中创建独立绑定,只会遮蔽全局对象的属性,不会修改它:let name = "张三"; console.log(window.name); // 输出原来的window.name值(比如空字符串或当前页面名称) console.log(name); // 输出"张三"
二、深层机制与区分的意义
这个区分绝非无意义,它是JS作用域模型进化的关键一步:
- 旧有机制的遗留问题
var属于ES3/ES5的函数作用域机制,早期JS没有块级作用域,全局对象被当作顶层作用域的载体,所以var声明的全局变量会挂载到全局对象上。但这种设计很容易导致变量泄漏、意外修改全局属性等问题。 - ES6的词法环境优化
ES6引入块级作用域后,顶层的let/const会被存储在顶层词法环境中,而非全局对象的属性。这让JS的作用域模型更接近传统编程语言,减少了“变量提升”“全局污染”这类反直觉的怪异行为。 - 为模块化铺路
在ES模块中,顶层的var/let都不会挂载到全局对象,而浏览器普通脚本中let的行为和模块保持一致,这为JS从全局式开发过渡到模块化开发提供了兼容性基础。
三、关于MDN定义的疑问
MDN的定义并没有过时,只是需要结合JS版本语境理解:
- ES5及以前,全局变量确实等同于全局对象的属性,因为当时只有
var和函数声明两种全局变量方式。 - ES6引入
let/const/class后,全局作用域出现了两类变量:- 绑定到全局对象的:
var、函数声明的全局变量,以及未声明直接赋值的变量(比如abc = 123)。 - 绑定到顶层词法环境的:
let、const、class声明的全局变量。
- 绑定到全局对象的:
- 不存在“二等全局变量”的说法,这两类变量都是全局作用域的成员,都能在所有作用域访问,只是存储位置和访问规则不同。而且顶层词法环境的变量优先级更高,会遮蔽全局对象的同名属性。
内容的提问来源于stack exchange,提问作者Chris Hall
相关产品推荐
相关产品推荐

