JavaScript变量提升与执行机制深度解析
本文深入解析JavaScript引擎的执行流程,重点阐述变量提升(Hoisting)与函数声明的提升行为。通过对比var声明与function定义在编译阶段的表现,揭示执行上下文中变量环境对象的创建过程。文章详细分析了重复声明的覆盖规则、undefined值的来源以及全局与函数执行上下文的生命周期,帮助开发者理解代码实际运行顺序与预期差异的根本原因。
变量提升
为深入理解变量提升机制,我们先分析以下代码片段:
showName()
console.log(myname)
var myname = '测试'
function showName() {
console.log('函数showName被执行');
}运行上述代码后,预期结果是什么?
按常规逻辑推演,执行第1行时,因函数showName尚未定义,预期会报错;同理,第2行引用未定义的变量myname也应报错。然而实际运行并未报错,且成功打印了结果。

这与预期不符。是否意味着在JavaScript中,未定义的变量和方法可直接使用?我们将注释掉myname的定义后重新运行代码以观察结果。

结果表明,在JavaScript中,变量或函数仍需定义后方可使用。
接下来观察以下代码的执行结果:
showName()
console.log('1: ',myname)
var myname = '测试'
console.log('2F: ',myname)
function showName() {
console.log('函数showName被执行');
}
由此可知,在JavaScript中:
1、若在执行过程中使用未声明的变量,JavaScript引擎会抛出错误。
2、在变量定义前使用它,不会报错,但该变量的值将为undefined,而非定义时的值。
3、在变量定义后使用它,其值为定义时的值。
4、在函数定义前使用它,不会报错,且函数能正常执行。
为何变量和函数在定义前使用不报错?这涉及JavaScript中的变量提升机制。
JavaScript中的声明与赋值:
语句varmyname = '测试'可拆解为变量声明与赋值两个步骤。
声明阶段:
var myname
赋值阶段:
myname = '测试'
函数的声明与赋值:
声明阶段:
function(){
console.log('函数showName被执行');
}
赋值阶段:
var showName = function(){
console.log('函数showName被执行');
}
补充说明:
函数先声明变量showName,再将function(){console.log('函数showName被执行');}赋值给showName。
变量提升是指在JavaScript代码执行过程中,JavaScript引擎将变量声明和函数声明部分提升至代码开头的一种行为。提升后,变量默认值为undefined。这也解释了为何第一段代码中myname打印值为undefined。
因此,第一段代码可理解为:

JavaScript代码执行流程
从字面意义看,“变量提升”意味着变量和函数声明在物理层面移动至代码最前端,如我们模拟的那样。但这并不准确。实际上,变量和函数声明在代码中的位置不变,而是在编译阶段由JavaScript引擎放入内存。
代码执行分为编译阶段与执行阶段。
编译阶段
基于上述理解,代码可分为变量提升部分与可执行部分。
变量提升部分的代码:
var myname = undefined
function showName() {
console.log('函数showName被执行');
}执行部分的代码:
showName()
console.log(myname)
myname = '极客时间'代码经编译后生成两部分内容:执行上下文与可执行代码。

执行上下文是JavaScript执行代码时的运行环境。例如调用函数时,会进入该函数的执行上下文,确定执行期间使用的this、变量、对象及函数等。
执行上下文中存在一个变量环境对象(Variable Environment),该对象保存变量提升的内容。例如上述代码中的变量myname和函数showName均保存于此对象中。
接下来,我们逐行分析变量环境对象的生成过程:
第1行和第2行因非声明操作,JavaScript引擎不做处理;
第3行经var声明,JavaScript引擎在环境对象中创建名为myname的属性,并用undefined初始化;
第4行JavaScript引擎发现通过function定义的函数,将其定义存储至堆(HEAP)中,并在环境对象中创建showName属性,指向堆中函数位置(若不了解堆也无妨,JavaScript的执行堆和执行栈将在后续文章介绍)。至此生成变量环境对象。随后,JavaScript引擎将声明以外的代码编译为字节码。
执行阶段
我们逐行分析执行过程:
(1)当执行到showName函数时,JavaScript引擎开始在变量环境对象中查找该函数。因对象中存在该函数引用,引擎开始执行该函数,并输出“函数showName被执行”。
(2)接下来打印“myname”信息,JavaScript引擎继续在变量环境对象中查找。因对象中存在myname变量且值为undefined,故输出undefined。
(3)接着执行第3行,将“极客时间”赋值给myname变量。赋值后,变量环境中的myname属性值变为“极客时间”。
代码中出现相同变量或函数怎么办
function showName() {
console.log('极客邦');
}
showName();
function showName() {
console.log('极客时间');
}
showName(); 上述代码的输出结果是什么?

我们分析其完整执行流程:
(1)首先是编译阶段。遇到第一个showName函数,将其函数体存入变量环境。接着遇到第二个showName函数,继续存入变量环境。因变量环境中已存在showName函数,第二个showName函数将覆盖第一个。此时变量环境中仅存第二个showName函数。
(2)接下来是执行阶段。先执行第一个showName函数,但因从变量环境中查找showName函数,而环境中仅保存第二个showName函数,故最终调用第二个函数,打印内容“极客时间”。第二次执行showName函数流程相同,输出结果亦为“极客时间”。综上,若代码定义两个同名函数,最终生效的是最后一个函数。
console.log('1:', myname)
var myname = 'zhangsan'
console.log('2:', myname)
var myname = 'lisi'
console.log('3:', myname)上述代码的执行结果又是怎样的?

分析:
(1)编译阶段。遇到第一个var myname时,将该变量存入变量环境,值为undefined。接着遇到第二个var myname时,继续存入变量环境,值亦为undefined。因变量环境中已存在myname变量,第二个myname将覆盖第一个。此时变量环境中仅存第二个myname变量,值为undefined。
(2)执行阶段。执行第一个console打印时,myname值为undefined,故打印undefined。直接对myname赋值,值为zhangsan,故执行第二个console时,打印zhangsan。接着执行第二条赋值语句,myname被赋值为lisi,故在执行第三个console时,打印lisi。
何种情况下会进行编译并创建执行上下文?
1、当JavaScript执行全局代码时,会编译全局代码并创建全局执行上下文。整个页面生命周期内,全局执行上下文仅有一份。
2、当调用函数时,函数体内代码会被编译,并创建函数执行上下文。一般情况下,函数执行结束后,创建的函数执行上下文会被销毁。
3、当使用eval函数时,eval代码会被编译,并创建执行上下文。
至此,关于Javascript浅析执行机制的详情文章介绍完毕。更多相关Js执行机制内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
