变量与函数提升

  1. 变量声明提升
  • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  • 值: undefined
  1. 函数声明提升
  • 通过function声明的函数, 在之前就可以直接调用
  • 值: 函数定义(对象)
  1. 先有变量提升, 再有函数提升

    1. /*变量提升*/
    2. console.log(a1) //可以访问, 但值是undefined
    3. /*函数提升*/
    4. a2() // 可以直接调用 输出a2()
    5. var a1 = 3
    6. function a2() {
    7. console.log('a2()')
    8. }

    测试题


/*
   面试题: 输出什么?
   */
  var a = 4
  function fn () {
    console.log(a)  //undefined 函数内部a变量提升
    var a = 5
  }
  fn()

  /*
  测试题1: 先预处理变量, 后预处理函数
  */
  function a() {}
  var a;
  console.log(typeof a)//function 变量提升后 函数提升覆盖了a

  /*
  测试题2: 变量预处理, in操作符
   */
  console.log(b in window); //true  {}无法生产作用域 b变量提升
  if (!(b in window)) {//进不来b的赋值不执行 但变量提升了 
    var b = 1;
  }
  console.log(b) //undefined 

  /*
  测试题3: 预处理, 顺序执行
   */
  var c = 1
  function c(c) {
    console.log(c)
    var c = 3
  }
  c(2) //c is no a function 解析如下

    //function c 函数提升时已经执行 上边的代码可以理解为
    //var c;
    //function c...
    //c=1
    //c(2)

全局&函数执行上下文

  • 理解
    • 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性
    • 执行上下文栈: 用来管理产生的多个执行上下文
  • 分类:
    • 全局: window
    • 函数: 对程序员来说是透明的
  • 生命周期
    • 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
    • 函数 : 调用函数时产生, 函数执行完时死亡
  • 包含哪些属性:

    • 全局 :
      • 用var定义的全局变量 ==>undefined
      • 使用function声明的函数 ===>function
      • this ===>window
    • 函数
      • 用var定义的局部变量 ==>undefined
      • 使用function声明的函数 ===>function
      • this ===> 调用函数的对象, 如果没有指定就是window
      • 形参变量 ===>对应实参值
      • arguments ===>实参列表的伪数组 ```javascript /*全局执行上下文**/
        console.log(a1) //undefined console.log(a2) //undefined console.log(a3) //ƒ a3() {} // console.log(a4) //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) //1 2 console.log(b1) //undefined console.log(b2) //ƒ b2 () {} console.log(arguments) //Arguments(2) [1, 2] console.log(this)//window

console.log(b3) //b3 is not defined

var b1 = 5
function b2 () {

}
b3 = 6

} fn(1,2)

<a name="bstmy"></a>
# 执行上下文栈

1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
1. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
1. 在函数执行上下文创建后, 将其添加到栈中(压栈)
1. 在当前函数执行完后,将栈顶的对象移除(出栈)
1. 当所有的代码执行完后, 栈中只剩下window

特点: **栈: 后进先出   **队列: 先进先出

> 图示代码在栈中执行过程

![image.png](https://cdn.nlark.com/yuque/0/2022/png/1624878/1648040363116-cb914cde-e877-4e44-acd4-a077c2e169e7.png#clientId=ua41cc2ef-fad2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=101&id=ub049f7f2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=201&originWidth=1027&originalType=binary&ratio=1&rotation=0&showTitle=false&size=74859&status=done&style=none&taskId=udda91fa0-91f8-48d4-838a-f485bb91022&title=&width=513.5)
```javascript
                            //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函数执行上下文

面试题

  1. 依次输出什么?

根据栈的特点后进先出,函数中递归导致压栈,所以会执行foo() begin先,而后foo() end再执行最后一个在栈最顶端最先执行

  1. global begin: undefined
  2. foo() begin: 1
  3. foo() begin: 2
  4. foo() begin: 3
  5. foo() end: 3
  6. foo() end: 2
  7. foo() end: 1
  8. global end: 1
  9. 整个过程中产生了几个执行上下文? 5个
    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); //i是形参不是外部的i
     console.log('foo() end:' + i);
    }
    console.log('global end: ' + i)