执行上下文与执行上下文栈
变量提升与函数提升
1.变量声明提升(先提升)
- 通过var定义(声明)的变量,在该定义语句之前就可以访问,但未赋值,返undefined!
- 值:undefined
2.函数声明提升
- 通过function声明的函数,在之前就可以直接调用,注意用函数表达式用的区别,表达式有var!
- 值:函数定义(对象)
var a = 3
function fn(){
//var a 变量提升 未赋值
console.log(a)//undefined
var a = 4//赋值
console.log(a)//4
}
fn()//undefined 4
/*****************/
console.log(b)// undefined 变量提升
fn2()//可调用 函数提升
fn3()//不能调用 没执行赋值操作不是函数,只是声明改变量 变量提升 var
var b = 3
function fn2() {//函数声明
console.log("fn2()")
}
var fn3 = function () {//函数表达式
console.log("fn3()")
}
执行上下文
1.代码分类(位置)
- 全局代码
- 函数(局部)代码
2.全局执行上下文(准备工作)
-
在执行全局代码前将window确定为全局执行上下文对象,只有一个!
- 对全局数据进行预处理 * var定义的全局变量==>undefined,未赋值,添加为window的属性,当执行到赋值语句时才会赋值,变量提升 * function声明的全局函数==>赋值(fun),添加为window的方法,函数提升
- this==>赋值(window)
-
开始执行全局代码
c = 3 //window中没有,没用var定义
console.log(a1,window.a1)// undefined undefined
a2() //a2()
console.log(this) //window
var a1 = 3
function a2() {
console.log("a2()")
}
console.log(a1) // 3
3.函数执行上下文
-
在调用函数,准备执行函数之前,创建对应的函数执行上下文对象(在栈空间中,非真实存在,封闭独有,注意与函数对象在堆空间区分)
-
对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为函数执行上下文的属性
- arguments(伪数组)==>赋值(实参列表),添加为函数执行上下文的属性
- var定义的局部变量==>undefined,添加为函数执行上下文的属性,变量提升
- function声明的函数==>赋值(fun),添加为函数执行上下文的方法,函数提升
- this==>赋值(调用函数的对象)
- 形参变量==>赋值(实参)==>添加为函数执行上下文的属性
-
开始执行函数体代码,这才正式开始执行函数!
【注】若是一个函数内嵌套两个函数,这两个函数是兄弟关系,则当其中一个函数调用执行时另一个并不执行,所有这里只会生成一个函数执行上下文对象!
function fn (a1) {
console.log(a1) //2
console.log(a2) // undefined
a3()//a
console.log(this) //window window.fn(2,3)
console.log(arguments) //伪数组(2,3)
var a2 = 3
function a3() {
console.log("a")
}
}
fn(2,3)
4.总结
n+1原则,n为调用函数次数,1为window,得出执行上下文个数
执行上下文栈
- 在全局代码执行前,JS引擎就会创建一个栈(后进先出,队列先进先出)来存储管理所有执行上下文对象
- 在全局执行上下文(window)确定后,将其添加到栈中,总在最底部(压栈)
- 在函数执行上下文创建后,将其压栈
- 在当函数执行完毕后,将栈顶的对象移除(出栈)
- 当所有代码执行完毕后,栈中只剩下window
var a = 10 //1.进入window全局执行上下文
var bar = function (x) {
var b = 5
foo(x+b)//3.进入foo函数执行上下文
}
var foo = function (y) {
var c = 5
console.log(a+c+y)
}
bar(10) //2.进入bar函数执行上下文
//函数每调用一次,生成一个函数执行上下文对象
面试题
console.log("ge:"+ i) //ge:undefined //1.进入window全局上下文
var i = 1
foo(1); //2.进入foo函数执行上下文
function foo(i) {
if(i == 4){
return;
}
console.log("fb:"+ i)
foo(i+1) //递归调用:在函数内部调用自己
console.log("fe"+ i)
}
console.log("ge"+ i) //ge:1
/*依次输出什么
gb:undefined
fb:1
fb:2 栈 (嵌套调用)
fb:3 X foo 4 return
fe:3 fe:3 foo 3 fb:3
fe:2 fe:2 foo 2 fb:2
fe:1 fe:1 foo 1 fb:1
ge:1 向下 ge:1 Window gb:undefined 向上
*/
//测试1 先执行变量提升,再执行函数提升
function a() {}
var a;//var a = 1 console.log(typeof a) number
console.log(typeof a)//"function"
//测试2
if (!(b in window)){
var b = 1
}
console.log(b)//undefined
//测试3
var c = 1
//var c
function c(c) {
console.log(c)
}
// c = 1
c(2) //error c不是函数