JS(JavaScript)执行过程
js的执行过程分为3大部分语法分析,预编译和执行阶段
语法分析: 分别对加载完成的代码块进行语法检验,语法正确则进入预编译阶段;不正确则停止该代码块的执行,查找下一个代码块并进行加载,加载完成再次进入该代码块的语法分析阶段
----JS预编译----
在js中变量分为全局变量和局部变量
局部变量: 在函数内部声明的变量都是局部变量(形参也是局部变量)
全局变量: 在js函数之外声明的变量是全局变量(注意:如果一个变量没有声明直接赋值,那么这个变量不管在哪都是全局变量,即使他是在函数内部)
var a=1; //a 全局变量
function fn(c){ //c 局部变量
var b=2; //b 局部变量
d=100; //d 全局变量
}
在js预编译阶段会将函数整体提升,变量声明提升
函数声明整体提升: 函数声明不管位于哪里,系统总会把声明提升到逻辑的最前面,因此无论函数调用在声明前或声明后结果都一样
变量声明提升:会把变量的声明提升到函数调用前面
console.log(a);
console.log(fn);
console.log(b);
console.log(f);
var a;
function fn(){
console.log("这是函数fn");
}
var b=function f(){
console.log("这是函数f");
}
运行结果发现var b=function f(){console.log(“这是函数f”);}中b声明被提升,
但是后面的函数f没有被整体提升,这是因为在预编译过程中遇到变量声明只会提升变量的声明并不会管对变量的赋值,即使是一个函数。
console.log(a);
console.log(fn);
var a;
function fn() {
console.log(b);
console.log("这是函数fn");
console.log(f);
var b=3;
function f(){
console.log("这是函数f");
}
}
fn();
结果显示在函数内也符合函数整体提升,变量声明提升
----JS执行上下文(Execution Context)-----
在浏览器中js是单线程的,在一个网页中运行js代码会产生一个全局执行上下文(Execution Context,以下简称EC),每进入一个新的函数(function)就会产生一个新的EC,并且会将这个产生的新EC压入执行上下文栈(ECStack)中,若函数中又调用了另一个函数,则再执行一次入栈…依次执行完再依次出栈,回到全局EC。全局EC一定是在栈底,在浏览器关闭后出栈。
EC的构成
EC由3部分构成分别为:变量对象VO(Variable Object)、作用域链(ScopeChain)和this
变量对象VO(Variable Object)保存此EC中涉及到的变量。
作用域链保存着此EC中的VO与其他EC中的VO的关联关系(能否访问到)。
然后是this,在EC被创建时,会确定this的指向。
当一个变量声明的时候会在他对应的EC中保存,函数则会为函数主体开辟一个堆用来存储函数,并在对应的EC中保存指向这个堆的地址。
EC中的scopeChain(作用域链),是由当前内存中各个变量对象VO串起来的单向链表,每入栈执行一个函数,其对应的VO就添加到作用域链的头部,前一个VO能自由访问到下一个VO上的变量,反过来就不行。
----闭包----
当一个函数执行完成后就会出栈,一般出栈后的EC就会被销毁,但是如果这时候有其他变量引用了被销毁的EC中VO的数据,那么这个EC并不会被立即销毁,从而形成一个闭包(以上为个人理解)
var a="这是全局变量a";
function f() {
var a="这是局部变量a";
console.log(a);
function fn(){
console.log(a);
}
return f;
}
var b=fn();
b();
按照EC出栈后会被立即销毁,那么调用b()会找不到在自己EC中的a会找到全局变量中的a,但是结果并没有找到全局中的a,而是局部变量中的a
闭包的好处:
希望一个变量长期存储在内存中。
避免全局变量的污染。
私有成员的存在。
闭包的缺点:
常驻内存,增加内存使用量。
使用不当会很容易造成内存泄露。