你是不是也经常被JS的执行顺序搞懵?
明明变量在后面定义,前面却能访问?
let/const和var到底有啥不一样?
函数执行时的变量查找规则总是记不住?
这篇文章用最通俗的语言+可运行的实战代码,带你彻底搞懂JS执行机制的核心逻辑。
读完你能解决:
- 变量提升的底层原因
- let/const的暂时性死区问题
- 函数编译执行的完整流程
- 块级作用域的实际应用场景
一、先搞懂:JS执行的两个核心阶段
JS代码不是简单从上到下执行,而是分为编译阶段和执行阶段。
这是理解所有执行机制的基础!
1. 编译阶段:提前做好"准备工作"
V8引擎在执行代码前,会先编译生成执行上下文对象,这个过程会做3件关键事:

- 从上面这张图片可以看到,执行上下文先运行环境再去执行代码
2. 执行阶段:按顺序执行代码
编译完成后,才会从上到下执行代码,给变量赋值、执行函数调用等操作。
二、实战1:变量提升与函数提升的坑
先看一段代码,猜猜执行结果?
// 实战代码1:变量提升
showName(hwq)
console.log(myname)
var myname = "hwq"
function showName(name) {
console.log("函数showName 执行了"+name)
}
执行结果:
函数showName 执行了hwq
undefined
底层原因:
编译阶段V8做了这些事:

踩坑提醒:
- 函数提升优先级 > 变量提升,这就是函数的优先
- var声明的变量会提升,值为undefined,容易导致预期外的undefined
再看一个更易踩坑的例子:
// 实战代码2:函数与变量同名
console.log(func)
function func(name) {
console.log("函数showName 执行了"+name)
}
var func = 'hwq'
执行结果:
[Function: func]
原因:
编译阶段函数声明先生效,变量声明不会覆盖函数,只有执行阶段的赋值才会改变。
三、实战2:函数执行时的变量处理
这段代码的执行结果是什么?
// 实战代码3:函数内的变量声明
var a =1;
function fn(a){
console.log(a)
var a=2
function a() {}
var b=a;
console.log(a)
}
fn(3);
执行结果:
[Function: a]
2
拆解编译过程(fn函数执行上下文):
- 第一个console.log(a) → 打印函数体
- 给a赋值2 → a变成2
- b赋值为a(2)
- 第二个console.log(a) → 打印2
踩坑提醒:
函数内部的变量/函数声明,编译阶段会先处理,执行阶段的赋值才会最终生效。
四、let/const:和var的核心区别
var的作用域是函数级,let/const是块级作用域,还有暂时性死区!
实战4:块级作用域对比
// 实战代码4:let的块级作用域
function varTest (){
var x=1;
if(true){
let x=2;
console.log(x) // 块内的x
}
console.log(x) // 函数级的x
}
varTest()
// 实战代码5:嵌套块级作用域
function foo(){
var a =1;
let b=2;
{
let b=3; // 块级作用域的b
var c=4; // var无块级作用域,属于函数级
let d=5; // 块级作用域的d
console.log(b) // 3
console.log(c) // 4
console.log(d) // 5
}
console.log(b) // 2(外层的b)
// console.log(d) // 报错:d is not defined
}
foo()
执行结果:
2
1
3
4
5
2

核心区别(加粗划重点):
- 重点提醒!:词法环境也是一种栈结构,这样可以看就近原则,且词法环境时有块级作用域,所以let const 不会像var那样把变量提升到外面的区域里面
踩坑提醒:
// 错误示例:let重复声明
let a=1;
let a=2; // 直接报错:Identifier 'a' has already been declared
五、调用栈:管理函数执行的"容器"
V8引擎用调用栈(执行栈) 管理函数调用关系:
通俗理解:
调用栈就像叠盘子,新调用的函数是最上面的盘子,执行完就拿走,始终操作最顶部的盘子。
六、总结:JS执行机制核心要点
掌握这些核心逻辑,你就能轻松解释90%的JS执行顺序问题,再也不会被变量提升、作用域坑到!





