前言
在JavaScript中,理解执行上下文、变量提升以及调用栈的概念对于深入掌握其运行机制至关重要。本文将围绕这三个核心概念进行详细解析,帮助读者更好地理解JavaScript代码的执行流程。
正文
1.声明提升
声明提升是JavaScript中一个独特的特性,它涉及到变量和函数声明在代码执行前被"提升"到其所在作用域的顶部。这分为两个主要部分:
(1) 变量声明提升:
当JavaScript引擎遇到变量声明(使用var
、let
、const
关键字)时,它会在当前作用域的顶部"声明"这些变量,但并不会初始化它们。这意味着变量虽然存在,但其值默认为undefined
,直到实际赋值语句执行时才改变。
我们来举个栗子
js
console.log(a);
var a=1;
若是在其他语言中先输出后声明,例如以上代码,代码则会报错。
但是JavaScript不会,正是因为其声明提升的特性,让以上代码能够正常运行 输出
(2)函数声明提升
与变量不同,使用函数声明语法定义的函数会整个提升至作用域顶部,包括函数名和函数体。这意味着你可以在声明之前调用函数,而不会引发错误。
我们来看一下这样一段代码
js
foo()
function foo(){
var a=1
console.log(a);
}
在这段代码中,先调用了函数,后才声明了函数
但是在JavaScript引擎中,它先是编译声明部分,后再运行操作,使得上述代码依旧能够执行不报错
这就是预编译中的声明提升!
2.函数中的预编译
每当一个函数被调用时,JavaScript会创建一个新的执行上下文------函数执行上下文,并进行以下步骤:
- 创建执行上下文对象(AO, Activation Object) :这是函数执行环境的具体体现,存储了函数执行时所需的信息。
- 处理形参和变量声明 :将函数的形参和内部声明的变量作为AO的属性,初始值设为
undefined
。 - 实参与形参的绑定:如果函数被调用时传递了实参,这些实参会覆盖AO中对应的形参属性值。
- 函数声明的处理:在函数体内部查找函数声明,并将其作为AO的属性,属性值为函数定义。
什么意思呢?我们来看这样一段代码:
js
function fn(a){
console.log(a);
var a=123
console.log(a);
function a(){
}
console.log(a);
var b =function(){}
console.log(b);
function d(){}
var d =a
console.log(d);
}
fn(1)
请问这段代码的执行结果是什么
我们可以按照我们上述所说的四步:
- 创建执行上下文对象AO
- 处理形参和变量声明,AO{a=undefined,b=undefined,d=undefined}
- 实参与形参的绑定,AO{a=1,b=undefined,d=undefined}
- 函数声明的处理,AO{a= function a(),b=function b(),d=undefined}
最后带着初始值将函数运行,结果为:
再来练习一小题,来看看它的运行结果是什么
js
function fn(a,b){
console.log(a);
c =0
var c;
a=3
b=2
console.log(b);
function b(){}
console.log(b);
}
fn(1)
答案是
做错的同学再思考一下吧!
3.全局的预编译
全局执行上下文(Global Execution Context)是所有非函数代码执行的环境,其初始化过程包括:
- 创建全局执行上下文对象(GO, Global Object) :在浏览器中,这通常是
window
对象。 - 变量声明 :将全局范围内使用
var
声明的变量(不包括let
和const
)作为GO的属性,初始值为undefined
。 - 函数声明:全局作用域中的函数声明同样会被提升,函数名作为GO的属性,属性值为函数的定义。
我们接着来看一段代码:
js
var global = 100
function fn() {
console.log(global);
}
fn()
这段函数的结果相信大家都没有疑问,函数体内没有声明global,所以访问全局变量global获取值,100
再来看看这一段代码
js
global = 100
function fn(){
console.log(global);
global = 200
console.log(global);
var global =300
}
fn()
var global;
我们开始按我们的步骤走
- 创建全局执行上下文对象BO{}
- 变量声明:BO{global=undefined}
- 函数声明:BO{global=undefined,function fn()}
接着我们再来函数的预编译:
- 创建执行上下文对象AO
- 处理形参和变量声明,AO{global=undefined}
- 实参与形参的绑定,AO{global=undefined}
- 函数声明的处理,AO{global=undefined}
然后开始执行,所以执行结果为:
一定要严格按照步骤走!
4.调用栈
调用栈是一种数据结构,用于跟踪函数的调用顺序和状态。在JavaScript中,每当一个函数被调用时,它的执行上下文会被压入调用栈顶;当函数执行完毕后,其执行上下文会被从栈顶弹出,控制权返回到上一层函数或全局作用域。这一过程确保了函数间的正确执行顺序和作用域隔离。
感谢观看!
求点赞评论收藏! 有问题随时私信博主!