本文讨论的预编译均为使用var进行声明的项目预编译。
在预编译这个阶段,JavaScript解析器会扫描整个代码,找出所有变量声明和函数声明,并在执行任何代码之前“预定义”它们。变量会被提升至所在作用域的顶部,但保持undefined值,直到赋值语句被执行;而函数声明则会被提升至作用域顶部,并且函数体也会被处理,这意味着在整个作用域内函数都可以被访问。
函数的预编译
函数的预编译执行上下文的创建过程:
1. 创建函数的执行上下文对象 (AO / Activation Object)
2. 找形参和变量声明,将形参和变量名作为AO的属性,值为undefined
3. 将实参和形参统一
4. 在函数体内部找函数声明,将函数名作为AO的属性名,值赋予函数体:
以下面的例子为讲解作为练习:
function fn(a) {
console.log(a); *//function a(){}*
var a = 123
console.log(a); *//123*
function a() {}
console.log(a); *//123*
var b = function () {} *//函数表达式*
console.log(b); *//function b() {}*
function d() {}
var d = a
console.log(d); *//123*
}
fn(1)
最后输出以次为:
[Function: a]
123
123
[Function: b]
123
过程可以理解为:
OA:{
*2.找形参变量声明 3.* *形参* *统一 4.函数声明 编译阶段*
a:undefined;------- -->1-------->function a(){}-------
b:undefined;--------------------->function (){}---------
d:undefined;--------------------->function d(){}--------
}
全局的预编译
全局的预编译执行上下文的创建过程:
1. 创建全局的执行上下文对象 (GO / Global Object)
2. 找变量声明,变量名作为GO的属性名,值为undefined
3. 在全局找函数声明,函数名作为GO的属性名,值为函数体
以下面的例子为讲解作为练习:
global = 100
function fn() {
console.log(global);
global = 200
console.log(global);
var global = 300
}
fn()
var global;
最后输出以次为:
undefined
200
过程可以理解为:
OG:{
*2.找形参变量声明 3.函数声明 编译阶段*
global:undefined;---->function fn(){}-----创建AO对象{
global:undefined
| 打印:undefined
V
global:200
打印:200
}
}
调用栈
简单来说,调用栈是一种数据结构,用于存储函数调用的信息,包括当前正在执行的函数以及它调用的所有函数的顺序和状态。在我们进行编译时,一开始会创建一个全局执行上下文的环境,在这个环境中存储着全局变量和全局函数的声明。当开始调用函数时,会再开辟一个该函数执行上下文的环境,可以理解为这个函数的上下文环境压在全局执行上下文的环境上面,而当该函数执行结束时,它开辟的环境也将释放,在这个函数没有被释放前,调用它的函数都无法被释放,因此可以将这种关系看作一个栈结构。
结构如下:
内部函数可以访问外部函数的变量也可以用这个自上而下的概念来理解。但是如果函数自身内部存在该变量,那么还是该变量优先被访问。