03. 函数高级-执行上下文与执行上下文栈
01. 变量提升与函数提升
1. 变量声明提升
* 通过var定义(声明)的变量, 在定义语句之前就可以访问到
* 值: undefined
2. 函数声明提升
* 通过function声明的函数, 在之前就可以直接调用
* 值: 函数定义(对象)
3. 问题: 变量提升和函数提升是如何产生的?
// 面试题: 输出什么? var a = 4 function fn1() { console.log(a) } function fn2() { console.log(a) var a = 5 } function fn3(a) { console.log(a) } function fn4(a) { console.log(a) var a = 5 } fn1() // 4 fn1(1) // 4 fn2() // undefined fn2(1) // undefined fn3() // undefined fn3(1) // 1 fn4() // undefined fn4(1) // 1 /*变量提升*/ console.log(a1) //可以访问, 但值是undefined var a1 = 3 /*函数提升*/ a2() // 可以直接调用 function a2() { console.log('a2()') }
02. 执行上下文
1. 代码分类(位置)
* 全局代码
* 函数代码
2. 全局执行上下文
* 在执行全局代码前将window确定为全局执行上下文
* 对全局数据进行预处理
* var定义的全局变量==>undefined, 添加为window的属性
* function声明的全局函数==>赋值(fun), 添加为window的方法
* this==>赋值(window)
* 开始执行全局代码
3. 函数执行上下文
* 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
* 对局部数据进行预处理
* 形参变量==>赋值(实参)==>添加为执行上下文的属性
* arguments==>赋值(实参列表), 添加为执行上下文的属性
* var定义的局部变量==>undefined, 添加为执行上下文的属性
* function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
* this==>赋值(调用函数的对象)
* 开始执行函数体代码
console.log(a1) // undefined console.log(a2) // undefined console.log(a3) // fun // console.log(a4) // error a4 is not defined console.log(this) // window var a1 = 3 var a2 = function () { console.log('a2()') } function a3() { console.log('a3()') } a4 = 4 function fn(x, y) { console.log(x, y) // undefined undefined console.log(b1) // undefined console.log(b2) // fun console.log(arguments) // arguments console.log(this) // window // console.log(b3) // error b3 is not defined var b1 = 5 function b2 () {} b3 = 6 } fn()
03. 执行上下文栈
1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
4. 在当前函数执行完后,将栈顶的对象移除(出栈)
5. 当所有的代码执行完后, 栈中只剩下window
//1. 进入全局执行上下文 var a = 10 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函数执行上下文
04. 执行上下文栈2
1. 依次输出什么?
2. 整个过程中产生了几个执行上下文?
console.log('global begin: '+ i) var i = 1 foo(1); function foo(i) { if (i == 4) { return; } console.log('foo() begin:' + i); foo(i + 1); console.log('foo() end:' + i); } console.log('global end: ' + i); // global begin: undefined // foo() begin:1 // foo() begin:2 // foo() begin:3 // foo() end:: 3 // foo() end:: 2 // foo() end:: 1 // global end: 1
05. 面试题
// 测试题1: 先预处理变量, 后预处理函数 function a() {} var a; console.log(typeof a) // function // 测试题2: 变量预处理, in操作符 if (!(b in window)) { var b = 1; } console.log(b) // undefined // 测试题3: 预处理, 顺序执行 var c = 1 function c(c) { console.log(c) var c = 3 } c(2) // c is not a function