概念
执行上下文(Execution Context):函数执行前进行的准备工作(也称上下文环境)
如确定作用域,创建局部变量对象等。
JavaScript中的执行环境
全局环境
函数环境
eval函数执行上下文
与之对应的执行上下文类型同样有3种:
全局执行上下文
函数执行上下文
eval函数执行上下文
由于JavaScript的单线程,同一时间段完成一件任务才可以继续下一个任务,那么如此,便必须要有一个排队机制。
通过栈的存取方式来管理上下文,可称之为执行栈,或函数调用栈。
栈数据结构的特点:
后进后出,先进后出
出口在顶部,且仅有一个
因为JavaScript执行中最先进入全局环境,所以处于"栈底的永远是全局环境的执行上下文"。而处于"栈顶的是当前正在执行函数的执行上下文"。
(function foo(i) {
if (i == 3) {
return;
} else {
console.log(i);
foo(++i);
}
})(0);
全局上下文
函数上下文 0
函数上下文 1
函数上下文 2
函数上下文
在上面的例子中,一共存在5个上下文,其中一个全局上下文,4个函数上下文
执行上下文的生命周期 ,两个阶段:
创建阶段(进行执行上下文):函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段
执行阶段(代码执行):执行函数中代码时,此时执行上下文进入执行阶段
创建阶段的操作
创建变量对象
函数环境会初始化创建Arguments对象,形式参数(并赋值)
普通函数声明(并赋值)
局部变量声明,函数表达式声明(未赋值)
初始化作用域链
确定this指向(this由调用者确定)
确定作用域(词法环境决定,哪里声明定义,就在哪里确定)
执行阶段的操作
变量对象赋值
变量赋值
函数表达式赋值
调用函数
顺序执行其他代码
执行上下文与作用域区别
变量对象
整个执行上下文的生命周期包含2个阶段建立阶段和执行阶段。
当处于执行上下文的建立阶段时我们可以将整个上下义环境看作是一个对象。 该对象拥有3个属性,如下:
executionContextObj ={
variableobject: {}
scopechain: {}
this: {}
这里执行上下义抽象成为了一个对象,拥有3个属性,分别是必量对象,作用域链以及this指向。
在函数的建立阶段,首先会建立Arguments对象确定形式参数,
检查当前上下文中的函数声明,每找到一个函数声明,就在variableObjcct下面用近函数名建立一个属性 ,属性值就指向该函数在内存中的地址的一个引用。
如果上述函数名已经存在于variablcObjcct (简称VO)下面,那么对应的属性值会被新的引用给覆盖。
最后,是确定当前上下文中的局部变量如果遇到和函数名同名的变量则会忽略该变量。
const foo = function(){
var a = "Hello";
var b = function privateB{};
function c(){}
}
foo (10);
首先在建立阶段的变量对象如下:
focExecutionContext ={
variavleObject:{
arguments: {0: 10, length: 1},
i: 10,
c: pointer to function c(),
a:undefined,
b:undefined /局部变量 初始值为undefined
},
scopeChain: {},
this: {}
}
首先在建立阶段的变量对象如下:
focExecutionContext ={
variavleObject:{
arguments: {0: 10, length: 1},
i: 10,
c: pointer to function c(),
a:undefined,
b:undefined /局部变量 初始值为undefined
},
scopeChain: {},
this: {}
}
由此可见,在建立阶段,除了Arguments,函数的声明.以及形式参数被赋予了具体的属性值,
其它的变量属性默认的都是undefined。
并且普通形式声明的函数的提升是在变量的上面的
一旦上述建立阶段结束,引擎就会进入代码执行阶段,这个阶段完成后,上述执行上下文对象如下,变量会被赋上具体的值
fooExecutlonContext ={
varlavleobject: {
arguments : {0: 10, Length : 1},
i: 10,
c: pointer to function c(),
a : "Hello",
b :pointer to funclion privaleB()
},
scopechain :{ },
this: {}
}
首先确定Arguments对象
接下来是形式参数
由于本例中不存在形式参数所以接下来开始确定函数的引用,找到foo()函数后,创建foo标创建foo()标识符来指向这个foo()函数
之后同名的foo变量不会再被创建会直接被忽略。然后创建bar变 量,不过初始值为undefined.
建立阶段完成之后,接下来进入代码执行阶段开始一句一句的执行代码,结果如下:
(function (){
console. log(typeof foo);
console. log(typeof bar);
var foo ="Hello";
var bar = function () {
return "World";
}
function foo() {
return "good";
}
console. log(foo, typeof foo);
})()