js执行上下文
4种情况会创建新的执行上下文
-
进入全局代码
-
进入function 函数体代码
-
进入eval函数参数指定的代码
-
进入module代码
全局对象和全局scope
全局声明的变量有两个存储地方,一个是全局对象中,一个是全局scope中。
var和function声明的变量会储存在全局对象中,而let和scope声明的变量会储存在全局的scope中。
var a=1;
function b(){}
let c=1
const d=1
定义在全局scope中的变量,我们是无法使用window.变量名获取到值的。注意,函数的文本环境只有它自身的scope没有全局对象。
代码执行流程
-
Step1 创建全局执行上下文,并加入栈顶
-
Step2 分析
- 找到所有的非函数中的var声明
- 找到所有的顶级函数声明(不在大括号内的函数声明)
- 找到顶级let,const,class声明
- 找到块中的声明,函数名不与上述重复
-
Step3 名字重复
- let,const,class声明的名字之间不能重发
- let声明的变量名不能重复
- const声明的变量名不能重复
- let和const声明的变量名不能重复
- let,const,class和var,function的名字不能重复
- let和var、function声明的变量名不能重复
- const和var、function声明的变量名不能重复
- var和function名字重复的,function声明的函数名变量提升优先于var,所以在声明前输出会打印函数,在声明后输出会打印var。
- let,const,class声明的名字之间不能重发
-
Step4 创建绑定
- 找到并初始化var和undefined
- 顶级函数声明:找到function名字,并初始化为新创建函数对象
- 块级中函数声明:找到名字,初始化为undefined
- 找到let,const,class,但未初始化
块中的函数声明
-
Step2 分析
- 找到块中的函数声明
-
Step3 名字重复
- 如果和找到的非函数的var声明、顶级函数声明、顶级let声明、顶级const声明、顶级class声明的名称重复,则不做任何处理。
- 如果不重复则在(如果是windown作用域则在全局对象,如果是函数作用域则在函数的scope)上创建一个以函数名为名的变量并初始化为undefined。
-
Step4
- 创建一个块的文本环境,将其连接到原来的文本环境中去。然后执行块中的流程
块中的代码执行流程
块中的代码执行流程和全局的基本一样,就在块中代码执行完后有不同的情况。
-
Step5 退出块
- 如果有函数作用域连接到了块的文本环境,则去全局对象中查看是否有同名的变量,如果有则将块中的变量赋值给全局。如果没有则不做任何处理。
let a=1;
if(true){
function a(){
console.log('函数');
}
}
console.log(a);//1
解释一下函数名和var名冲突
我们在上面说了。
var和function名字重复的,function声明的函数名变量提升优先于var,所以在声明前输出会打印函数,在声明后输出会打印var。
我们看下面的代码:
console.log(a);//f a(){}
var a=1;
function a(){
console.log(1);
}
console.log(a);//1
因为函数和var重名,函数变量提升优先于var所以第一次输出a返回函数。第二次输出a由于var变量提升在var之后,所以,var在函数之后将函数覆盖,最后输出1。