严格模式下对未声明变量赋值或读取会抛出ReferenceError,非严格模式下仅赋值会向全局对象创建属性;作用域链仅用于查找,不包含全局对象,全局写入是独立兜底机制。

在 JavaScript 中,当代码访问一个未声明的变量时,引擎会沿着作用域链向上查找,直到全局作用域;如果仍未找到,**严格模式下直接抛出 ReferenceError,非严格模式下则尝试在全局对象(如浏览器中的 window 或 Node.js 中的 globalThis)上创建该变量并赋值**。
未声明变量在非严格模式下的“自动挂载”行为
这是 JavaScript 早期设计的一个容易引发 bug 的特性:
- 执行
foo = 42;(没有var/let/const)时,JS 引擎发现foo不在当前作用域、外层作用域……最终查到全局作用域也不存在 - 此时,引擎将该变量作为**全局对象的一个可写属性**进行创建(例如浏览器中等价于
window.foo = 42;) - 这个过程不报错,但污染全局命名空间,且难以追踪来源
严格模式彻底禁止该行为
在 "use strict"; 下,任何对未声明变量的赋值或读取都会立即报错:
-
"use strict"; bar = 100;→ ReferenceError: bar is not defined - 这强制开发者显式声明变量,避免隐式全局变量
- 推荐在所有脚本顶部启用严格模式,或使用 ES 模块(默认严格模式)
全局对象不是“作用域链的顶端”,而是兜底写入目标
需要澄清一个常见误解:
立即学习“Java免费学习笔记(深入)”;
- 作用域链只用于 标识符解析(变量查找),它由词法嵌套决定,不包含全局对象本身
- 当查找失败后,**赋值操作**才会触发对全局对象的写入(仅非严格模式),这不是作用域链的一部分,而是一个独立的兜底机制
- 读取未声明变量(如
console.log(baz);)在非严格模式下同样报错 —— 只有**赋值**才触发自动挂载
如何避免意外创建全局变量
实际开发中应主动规避这类问题:
- 始终使用
let/const声明变量(块级作用域 + 不允许重复声明 + 不提升) - 启用严格模式(
"use strict";或使用.mjs/type="module") - 借助 ESLint 规则(如
no-implicit-globals、no-undef)在编码阶段拦截 - 避免在函数内省略关键字直接赋值,尤其注意循环、回调、事件处理函数中易遗漏声明


















