【進階1-1期】理解JavaScript 中的執行上下文和執行棧

更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看前端

------ 如下是正文 ------node

本期的主題是調用堆棧,本計劃一共28期,每期重點攻克一個面試重難點,若是你還不瞭解本進階計劃,文末點擊查看所有文章。webpack

若是以爲本系列不錯,歡迎點贊、評論、轉發,您的支持就是我堅持的最大動力。git


執行上下文是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念。github

執行上下文的類型

執行上下文總共有三種類型web

  • 全局執行上下文:只有一個,瀏覽器中的全局對象就是 window 對象,this 指向這個全局對象。面試

  • 函數執行上下文:存在無數個,只有在函數被調用的時候纔會被建立,每次調用函數都會建立一個新的執行上下文。算法

  • Eval 函數執行上下文: 指的是運行在 eval 函數中的代碼,不多用並且不建議使用。跨域

執行棧

執行棧,也叫調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。數組

首次運行JS代碼時,會建立一個全局執行上下文並Push到當前的執行棧中。每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並Push到當前執行棧的棧頂。

根據執行棧LIFO規則,當棧頂函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文控制權將移到當前執行棧的下一個執行上下文。

var a = 'Hello World!';

function first() {  
  console.log('Inside first function');  
  second();  
  console.log('Again inside first function');  
}

function second() {  
  console.log('Inside second function');  
}

first();  
console.log('Inside Global Execution Context');

// Inside first function
// Inside second function
// Again inside first function
// Inside Global Execution Context
複製代碼

執行上下文的建立

執行上下文分兩個階段建立:1)建立階段; 2)執行階段

建立階段

  • 一、肯定 this 的值,也被稱爲 This Binding

  • 二、LexicalEnvironment(詞法環境) 組件被建立。

  • 三、VariableEnvironment(變量環境) 組件被建立。

直接看僞代碼可能更加直觀

ExecutionContext = {  
  ThisBinding = <this value>, // 肯定this LexicalEnvironment = { ... }, // 詞法環境 VariableEnvironment = { ... }, // 變量環境 } 複製代碼
This Binding
  • 全局執行上下文中,this 的值指向全局對象,在瀏覽器中this 的值指向 window對象,而在nodejs中指向這個文件的module對象。

  • 函數執行上下文中,this 的值取決於函數的調用方式。具體有:默認綁定、隱式綁定、顯式綁定(硬綁定)、new綁定、箭頭函數,具體內容會在【this全面解析】部分詳解。

詞法環境(Lexical Environment)

詞法環境有兩個組成部分

  • 一、環境記錄:存儲變量和函數聲明的實際位置

  • 二、對外部環境的引用:能夠訪問其外部詞法環境

詞法環境有兩種類型

  • 一、全局環境:是一個沒有外部環境的詞法環境,其外部環境引用爲 null。擁有一個全局對象(window 對象)及其關聯的方法和屬性(例如數組方法)以及任何用戶自定義的全局變量,this 的值指向這個全局對象。

  • 二、函數環境:用戶在函數中定義的變量被存儲在環境記錄中,包含了arguments 對象。對外部環境的引用能夠是全局環境,也能夠是包含內部函數的外部函數環境。

直接看僞代碼可能更加直觀

GlobalExectionContext = {  // 全局執行上下文
  LexicalEnvironment: {    	  // 詞法環境
    EnvironmentRecord: {   		// 環境記錄
      Type: "Object",      		   // 全局環境
      // 標識符綁定在這裏 
      outer: <null>  	   		   // 對外部環境的引用
  }  
}

FunctionExectionContext = { // 函數執行上下文
  LexicalEnvironment: {  	  // 詞法環境
    EnvironmentRecord: {  		// 環境記錄
      Type: "Declarative",  	   // 函數環境
      // 標識符綁定在這裏 			  // 對外部環境的引用
      outer: <Global or outer function environment reference>  
  }  
}
複製代碼
變量環境

變量環境也是一個詞法環境,所以它具備上面定義的詞法環境的全部屬性。

在 ES6 中,詞法 環境和 變量 環境的區別在於前者用於存儲**函數聲明和變量( letconst綁定,然後者僅用於存儲變量( var )**綁定。

使用例子進行介紹

let a = 20;  
const b = 30;  
var c;

function multiply(e, f) {  
 var g = 20;  
 return e * f * g;  
}

c = multiply(20, 30);
複製代碼

執行上下文以下所示

GlobalExectionContext = {

  ThisBinding: <Global Object>,

  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Object",  
      // 標識符綁定在這裏  
      a: < uninitialized >,  
      b: < uninitialized >,  
      multiply: < func >  
    }  
    outer: <null>  
  },

  VariableEnvironment: {  
    EnvironmentRecord: {  
      Type: "Object",  
      // 標識符綁定在這裏  
      c: undefined,  
    }  
    outer: <null>  
  }  
}

FunctionExectionContext = {  
   
  ThisBinding: <Global Object>,

  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Declarative",  
      // 標識符綁定在這裏  
      Arguments: {0: 20, 1: 30, length: 2},  
    },  
    outer: <GlobalLexicalEnvironment>  
  },

  VariableEnvironment: {  
    EnvironmentRecord: {  
      Type: "Declarative",  
      // 標識符綁定在這裏  
      g: undefined  
    },  
    outer: <GlobalLexicalEnvironment>  
  }  
}
複製代碼

變量提高的緣由:在建立階段,函數聲明存儲在環境中,而變量會被設置爲 undefined(在 var 的狀況下)或保持未初始化(在 letconst 的狀況下)。因此這就是爲何能夠在聲明以前訪問 var 定義的變量(儘管是 undefined ),但若是在聲明以前訪問 letconst 定義的變量就會提示引用錯誤的緣由。這就是所謂的變量提高。

執行階段

此階段,完成對全部變量的分配,最後執行代碼。

若是 Javascript 引擎在源代碼中聲明的實際位置找不到 let 變量的值,那麼將爲其分配 undefined 值。

參考

理解 Javascript 執行上下文和執行棧

進階系列目錄

  • 【進階1期】 調用堆棧
  • 【進階2期】 做用域閉包
  • 【進階3期】 this全面解析
  • 【進階4期】 深淺拷貝原理
  • 【進階5期】 原型Prototype
  • 【進階6期】 高階函數
  • 【進階7期】 事件機制
  • 【進階8期】 Event Loop原理
  • 【進階9期】 Promise原理
  • 【進階10期】Async/Await原理
  • 【進階11期】防抖/節流原理
  • 【進階12期】模塊化詳解
  • 【進階13期】ES6重難點
  • 【進階14期】計算機網絡概述
  • 【進階15期】瀏覽器渲染原理
  • 【進階16期】webpack配置
  • 【進階17期】webpack原理
  • 【進階18期】前端監控
  • 【進階19期】跨域和安全
  • 【進階20期】性能優化
  • 【進階21期】VirtualDom原理
  • 【進階22期】Diff算法
  • 【進階23期】MVVM雙向綁定
  • 【進階24期】Vuex原理
  • 【進階25期】Redux原理
  • 【進階26期】路由原理
  • 【進階27期】VueRouter源碼解析
  • 【進階28期】ReactRouter源碼解析

交流

進階系列文章彙總:github.com/yygmind/blo…,內有優質前端資料,歡迎領取,以爲不錯點個star。

我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!

相關文章
相關標籤/搜索