JavaScript 作用域机制深度解析:从变量提升至作用域链

作者:代码拾荒

时间:2026-10-26

浏览:112

本文深入解析 JavaScript 中的作用域机制,涵盖全局与函数级作用域的区别。通过代码示例阐述变量提升(Hoisting)现象、函数定义式与表达式的差异,以及作用域链在运行时的行为。重点说明为何 JS 不存在块级作用域,并解释函数在定义时而非调用时绑定作用域的原理。

文章目录

JavaScript 作用域基础

JavaScript 中的作用域概念:



作用域定义了变量或函数生效的范围,即它们所处的上下文执行环境。在 JavaScript 中,作用域主要分为两种:全局作用域和局部(函数)作用域。其中,局部作用域是以函数为单位进行划分的。



首先通过几个典型示例来观察:



1.


代码如下:

 
if(true){
var aa= "bb";
} 
console.log(aa); //bb
for(var i = 0; i < 100; i++){
//do
} 
console.log(i); //100

2.


代码如下:

 
var bb = '11111';
function aa() {
alert(bb);//undefine
var bb = 'test';
alert(bb);//test
var cc = "test1";
alert(age);//语法错误
} 
aa();

3.


代码如下:

 
var test = '1111111';
function aa() {
alert(test);
} 
function bb() {
var test = '22222222';
aa();
} 
bb();//alert(1111111);

4.


代码如下:

 
alert(typeof aa); //function
alert(typeof bb); //undefined function aa() { //函数定义式
alert('I am 111111111');
}; 
var bb = function() { //函数表达式
} 
alert(typeof bb);//function

5.


代码如下:

 
function aa(){
var bb = "test";
cc = "测试";
alert(bb);
} 
aa();
alert(cc);//测试
alert(bb);//语法报错

作用域核心机制解析

上述五个示例涵盖了 JavaScript 作用域的核心问题。



可以总结出以下关键观点:



无块级作用域特性

一、无块级作用域



从第一个示例可以看出,在代码块 {} 中执行的变量并未在块结束后销毁,而是保留在内存中,因此可以在外部访问到。



函数定义与作用域绑定

二、JavaScript 中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。



这里涉及“作用域链”的概念。在 ECMAScript 262 规范中规定:



任何执行上下文时刻的作用域,均由作用域链(scope chain)实现。


在函数被定义时,会将定义时的 scope chain 链接到该函数对象的 [[scope]] 属性中。


当函数对象被调用时,会创建一个活动对象(即一个对象)。对于函数的每个形参,将其命名为该活动对象的命名属性,并将此活动对象作为当前作用域链(scope chain)的最前端,同时将函数对象的 [[scope]] 加入 scope chain 中。


因此,示例 3 的结果为 alert(1111111);



变量提升与预编译行为

三、JavaScript 会提前处理 function 定义式和 var 关键字



如示例 4 所示,初始 alert(bb) 返回 undefined,而 alert(age) 报语法错误。两者的区别在于:后续存在 var bb = "test",在初始化阶段提前处理了 var 关键字,但该变量此时尚未赋值。



将代码修改如下,可以观察到:


代码如下:

 
var dd = '11111';
function aa() {
alert(bb);//undefine
var bb = 'test';
alert(bb);//test
var cc = "test1";
alert(age);//语法错误
} 
aa();
alert(dd);//11111
alert(cc);//语法报错

此处 alert(bb) 未报语法错误,而 alert(age) 报语法错误。



但请注意:


代码如下:

 
<script>
alert(typeof aa); //结果:undefined
</script>
<script>
function aa() {
alert('yupeng');
} 
</script>

这表明 JavaScript 的预编译是以脚本段(script block)为单元的。示例 4 同理。



函数级作用域生命周期

四、函数级作用域



在函数内部定义的变量,在函数执行完毕后即被销毁,不再占用内存区域。



因此,示例 2 最后的 alert(cc) 报语法错误,示例 5 最后的 alert(bb) 同理。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章