JavaScript 作用域机制深度解析:从变量提升至作用域链
本文深入解析 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) 同理。
JavaScript异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
