JavaScript 函数高级——执行上下文与执行上下文栈(图解+典型实例分析)
变量提升与函数提升
-
变量声明提升
- 通过
var
定义(声明)的变量,在定义语句之前就可以访问到 - 值:
undefined
- 通过
/*
面试题 : 输出 undefined
*/
var a = 3
function fn() {
console.log(a) // undefined 调用fn()后进入函数体内部,通过 var 声明的变量提升,值为undefined
var a = 4
console.log(a) // 4
}
fn()
console.log('----')
可以用开发工具的代码调试工具设置断点,清楚地看到
a
的值的变化。
-
函数声明提升
- 通过
function
声明的函数,在之前就可以直接调用 - 值:函数定义(对象)
- 通过
console.log(b) // undefined 通过 var 声明的变量b 变量声明提升
fn2() // fn2() 通过 function 声明的函数fn2 函数声明提升
fn3() // 报错 通过 var 声明的函数fn3 变量声明提升
var b = 3
function fn2() {
console.log('fn2()')
}
var fn3 = function () {
console.log('fn3()')
}
!!注意 var a = 3
, var fn = function(){ }
只要是var
声明的,都是变量声明提升,值都是undefined
!!注意:var fn = function(){ }
属于变量声明提升,不是函数声明提升
- 问题:变量提升和函数提升是如何产生的?
执行上下文
// 变量声明提升,函数声明提升,全局代码的函数和方法添加为Window的方法和属性
console.log(a1, window.a1) // undefined undefined
window.a2() // a2()
console.log(this) // Window
var a1 = 3
function a2() {
console.log('a2()')
}
console.log(a1) // 3
代码分类(位置)
- 全局代码
- 函数代码
全局执行上下文
- 在执行全局代码前将
window
确定为全局执行上下文 -
对全局数据进行预处理:
-
var
定义的全局变量==>undefined
,添加为window
属性 -
function
声明的全局函数==>赋值(fun
),添加为window
的方法 -
this
==>赋值(window
)
-
- 开始执行全局代码
函数执行上下文
- 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
-
对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为执行上下文的属性
-
arguments
==>赋值(实参列表),添加为执行上下文的属性 -
var
定义的局部变量==>undefined
==>添加为执行上下文的属性 -
function
声明的函数==>赋值(fun
)==>添加为执行上下文的属性 -
this
==>赋值(调用函数的对象)
- 开始执行函数体代码
执行上下文栈
理解
- 在全局代码执行前,
JS
引擎就会创建一个栈来存储管理所有的执行上下文对象 - 在全局执行上下文(
window
)确定后, 将其添加到栈中(压栈) - 在函数执行上下文创建后, 将其添加到栈中(压栈)
- 在当前函数执行完后,将栈顶的对象移除(出栈)
- 当所有的代码执行完后, 栈中只剩下
window
流程分析
// 1. 进入全局上下文
var a = 10
var bar = function (x) {
var b = 5
// 3. 进入foo执行上下文
foo(x + b)
}
var foo = function (y) {
var c = 5
console.log(a + c + y)
}
// 2. 进入bar函数执行上下文
bar(10) // 30
- 用断点调试工具查看执行上下文调用栈
面试题
题1:递归调用的执行上下文
console.log('gb: ' + i)
var i = 1
foo(1)
function foo(i) {
if (i == 4) {
return
}
console.log('fb:' + i)
foo(i + 1) //递归调用: 在函数内部调用自己
console.log('fe:' + i)
}
console.log('ge: ' + i) // ge:1
- 依次输出什么?
- 整个过程中产生了几个执行上下文?
5个
=4个函数执行上下文
+1个全局执行上下文
调试查看:
题2:先执行变量提升,后执行函数提升
function a() { }
var a
console.log(typeof a) // 'function'
题3
if (!(t in window)) {
var t = 1
}
console.log(t) // undefined
t in window
// true
题4(好题)
var c = 1
function c(c) {
console.log(c)
var c = 3
}
c(2) // 报错 c is not a function
注意:函数提升为c()
,整个函数提升,所以上面的代码相当于下面这样,这样就很清楚了吧!
var c
function c(c) {
console.log(c)
var c = 3
}
c = 1
c(2) //所以 c is not a function
函数体内的代码根本就没机会执行。
发表回复