【6】JavaScript 函數高級——執行上下文與執行上下文棧、變量提升(圖解+典型實例分析)

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 = 3var 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
  • 用斷點調試工具查看執行上下文調用棧

clipboard.png

clipboard.png

clipboard.png

clipboard.png

面試題

題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
  1. 依次輸出什麼?

clipboard.png

  1. 整個過程中產生了幾個執行上下文?

    5個 = 4個函數執行上下文 + 1個全局執行上下文

調試查看:
clipboard.png

題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

函數體內的代碼根本就沒機會執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章