深入理解JavaScript作用域:变量提升与查找机制
本文深入解析JavaScript的作用域机制,重点阐述函数级作用域与块级作用域的区别。通过变量提升(预解析)和就近查找原则,解释变量在函数内外访问时的行为差异。文章结合代码示例,分析参数传递、变量重名优先级及引用类型赋值对作用域链的影响,帮助开发者避免常见的作用域错误。
JavaScript作用域基础与黄金法则
理解JavaScript作用域的第一条黄金法则:
JavaScript原生环境中不存在块级作用域(开发者可通过闭包或特定语法自行实现),其作用域粒度仅为函数级别。这意味着定义在函数外部的变量可以在函数内部被访问,而定义在函数内部的变量则无法在外部直接获取。
首先进行初步尝试:

这一现象背后的原因是什么?
var a = 10;
function aaa(){//step-4
alert(a);//step-5->执行alert,此时只能找到外面的a=10故弹框10
}
function bbb(){//step-2
var a = 20;
aaa();//step-3
}
//定义了函数没啥用,调用才是真格的所以这里是step-1
bbb();//step-1
其核心原理大家应当熟悉,这通常是一个容易出错的知识点,但万变不离其宗。
接着进行第二次尝试:

原因在于,在将变量b赋值给a时,变量b尚未完成定义。因此,此时a的值为undefined,而b的值为10。
变量查找机制与预解析原理
第二条黄金法则:
变量的查找遵循“就近原则”。引擎会首先在当前作用域内寻找由var声明的变量;若在当前作用域中未找到,则沿着作用域链向外层作用域继续查找。
观察以下示例:

产生该结果的原因有两个:一是变量的预解析机制,二是作用域的就近查找规则。
var a=10;
function aaa(){
alert(a);//undefined,查找a的时候会现在函数内查找,由于预解析的作用,此时的a是undefined,因此永远不会去查找外面的10了
var a = 20;
/*预解析
var a
alert(a);
var a = 20;*/
}
aaa();
需要注意的细节:

这验证了上述第二条法则。尽管遵循就近原则,但引擎仅查找var声明的变量。未使用var声明的变量被视为全局变量,此处仅修改了全局a的值。因此,在函数内部未找到var声明的a时,引擎向外层查找并找到了全局a,导致alert输出10。需要注意的是,虽然a在后续被赋值为20,但在执行alert语句时尚未运行到该赋值操作。
继续观察:

下面的例子进一步验证了JavaScript仅具备函数作用域的特性:

当执行alert(a)时,bbb函数内部的a确实为20。然而,对于当前的alert(a)语句而言,bbb函数中的a属于局部作用域,当前作用域无法直接访问bbb函数内部的a。因此,引擎在aaa函数中未找到a,进而向外层查找,最终找到了值为10的全局变量a。
参数优先级与传值传引用分析
第三条黄金法则:
当函数参数与局部变量重名时,两者的优先级是等同的。
具体示例如下:

此外,在传参时,基本数据类型按值传递,引用数据类型按引用传递(但在重新赋值后,行为会有所不同)。
var a = 5;
var b = a;
b +=3;
alert(a);//5
var a = [1,2,3];
var b=a;
b.push(4);
alert(a);//[1,2,3,4];
上述代码运行正常,但下方的代码行为则有所不同。

这是因为变量b被重新赋值,不再指向原变量a。
此外,参数与局部变量的作用域表现相似:

对比上下两组代码:

上方示例中参数为基本类型,仅传递了值;下方示例传递的是引用类型(同样包含重新赋值的情况)。


core-js v2 迁移到 v3 的破坏性变更与注意事项
2026-11-05 18:10:26
JavaScript中%是取余还是取模?与Math.floor实现的区别
2026-11-05 18:09:12
React中怎么不可变地删除JS数组元素
2026-11-05 18:06:01
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
