变量提升+函数提升+执行上下文

210310

一、变量声明提升
  • 通过var定义(声明)的变量,在定义语句之前就可以访问到
  • 值:undefined
二、函数声明提升
  • 通过function声明的函数,在之前就可以直接调用
  • 值:函数定义(对象)
var a = 3
function fn(){
    /*相当于先声明了a var a;a = undefined
    先在自身函数中找变量,再去全局找
    */
    console.log(a) //undefined
    var a = 4
}
fn()  //undefined

console.log(b) //undefined //变量提升
fn2() //可调用 函数提升
fn3() //变量提升,不可执行
var b = 3
function fn2(){
    console.log('fn2')
}
var fn3 = function(){
    console.log('fn3')
}

注意:

  • 函数声明优先级最高
    function fn(){}
  • 其次:函数表达式和变量,按顺序执行
    ①匿名函数表达式:fn=function(){}
    ②具名函数表达式:fn=function bar(){}
    ③变量:var a

测试一:

function a(){}
var a
console.log(typeof a)  //function

function a(){}
var a = 1
console.log(typeof a)  //number

测试二:

var c = 1  //=>①var c
function c(c){
    console.log(c)
    var c = 3  //无关
}
//=>②c = 1 =>c typeof number
c(2) //报错c is not a function
三、执行上下文
  1. 代码分类(位置)

    • 全局代码
    • 函数(局部)代码
  2. 全局执行上下文

    • 在执行全局代码前将window确定为全局执行上下文

    • 对全局数据进行预处理(收集数据)

      • var定义的全局变量==>(初始化赋值)undefined,添加为window的属性
      • function声明的全局函数==>赋值(函数对象),添加为window的方法
      • this==>赋值(window)
    • 开始执行全局代码

      c = 3 //特殊:未执行语句之前,window中不包含此变量,需用var定义
      console.log(a1,window.a1) //undefined
      a2() //a2()
      console.log(this) //window
      var a1 = 2
      function a2(){
          console.log('a2()')
      }
      console.log(a1) //2
      
  3. 函数执行上下文

    • 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)

    • 对局部数据进行预处理

      • 形参变量>赋值(实参)>添加为执行上下文的属性
      • arguments==>赋值(实参列表),添加为执行上下文的属性
      • var定义的局部变量==>undefined,添加为执行上下文的属性
      • function声明的函数==>赋值(函数对象),添加为执行上下文的方法
      • this==>赋值(调用函数的对象)
    • 开始执行函数体代码

      function f1(a1){
          console.log(a1)
          console.log(a2)
          a3()
          console.log(this)
          console.log(arguments)
          
          var a2 = 3
          function a3(){
              console.log('a3()')
          }
      }
      f1(2,3) //2,undefined,a3(),window,2 3
      

210314执行上下文栈

  1. 在全局代码运行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  3. 在函数执行上下文创建后,将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移出(出栈)
  5. 当所有的代码执行完毕,栈中只剩下window
//调用一次函数产生一次执行上下文栈n+1(window)
//1、进入window全局执行上下文
var a = 10
var bar = function(x){
    var b = 5
    //2、进入foo执行上下文
    foo(x + b)
}
var foo = function(y){
    var c = 5
    console.log(a + c + y)
}
//3、进入bar函数执行上下文
bar(10)  //30

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
整个过程中产生了五个执行上下文
*/

210315执行上下文的作用域n(定义的函数)+1(全局)

有n(调用函数的次数)+1(window)执行上下文

  1. 理解

    • 就是一块“地盘”,一个代码段所在的地区
    • 是静态的(相对于上下文对象),在编写代码时就确认了
  2. 分类

    • 全局作用域
    • 函数作用域
    • 没有块作用域(ES6有)
    if(true){
        var c = 3
    }
    console.log(c) //3 全局中有c变量,没有块作用域
    

    image

    image

  3. 作用

    • 隔离变量,不同作用域下同名变量不会有冲突
posted @ 2021-07-14 09:38  STRIVE-PHY  阅读(78)  评论(0编辑  收藏  举报