日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

變量提升

實際上變量和函數聲明在代碼里的位置是不會變的,而且是在編譯階段被 JAVAScript 引擎放入內存中,一段 JavaScript 代碼在執行之前需要被 JavaScript 引擎編譯,編譯完成之后,才會進入執行階段。大致流程為:JavaScript 代碼片段 ——> 編譯階段 ——> 執行階段—>

編譯階段,每段執行代碼會分為兩部分,第一部分為變量提升部分的代碼,第二部分為執行部分的代碼。經過編譯后,生成執行上下文(Execution context)和 可執行代碼

瀏覽器中 JavaScript 的執行機制

 

執行上下文 是 JavaScript 執行一段代碼時的運行環境,比如調用一個函數,就會進入函數的執行上下文,從而確定該函數執行期間用到的如 this、變量、對象以及函數等。

執行上下文由 變量環境(Variable Environment) 和 **詞法環境(Lexical Environment)**對象 組成,變量環境保存了代碼中變量提升的內容,包括 var 定義和 function 定義的變量。而詞法環境保存 let 和 const 定義塊級作用域的變量。

瀏覽器中 JavaScript 的執行機制

 

塊級作用域就是通過詞法環境的棧結構來實現的,而變量提升是通過變量環境來實現,通過這兩者的結合,JavaScript 引擎也就同時支持了變量提升和塊級作用域了

變量查找過程:沿著詞法環境的棧頂向下查詢,如果在詞法環境中的某個塊中查找到了,就直接返回給 JavaScript 引擎,如果沒有查找到,那么繼續在變量環境中查找。

變量聲明提升補充:

  • var的創建和初始化被提升,賦值不會被提升。
  • let的創建被提升,初始化和賦值不會被提升。
  • function的創建、初始化和賦值均會被提升。

調用棧

調用棧是用來管理函數調用關系的一種數據結構。在函數調用的時候,JavaScript 引擎會創建函數執行上下文,而全局代碼下又有一個全局執行上下文,這些執行上下文會使用一種叫的數據結果來管理。

所以 JavaScript 的調用棧,其實就是 執行上下文棧 。舉例代碼執行,入棧如圖所示:

var a = 2
function add(b,c){
  return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return  a+result+d
}
addAll(3,6)
瀏覽器中 JavaScript 的執行機制

 

調用棧既然是一種數據結構,所以是存在大小的,超出了棧大小就會出現棧溢出報錯,比如斐波那契數列,執行10000次,超過了最大棧調用大小(Maximum call stack size exceeded)。

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(10000) // Maximum call stack size exceeded

該函數是遞歸的,雖然只有一種函數調用,但是還是會一直創建執行上下文壓入調用棧中,導致超過最大調用棧大小報錯,可以通過 Chrome 調式看到 Call Stack 的情況

 

瀏覽器中 JavaScript 的執行機制

 

總結:

  • 每調用一個函數,JavaScript 引擎會為其創建執行上下文,并把該執行上下文壓入調用棧,然后 JavaScript 引擎開始執行函數代碼。
  • 如果在一個函數 A 中調用了另外一個函數 B,那么 JavaScript 引擎會為 B 函數創建執行上下文,并將 B 函數的執行上下文壓入棧頂。
  • 當前函數執行完畢后,JavaScript 引擎會將該函數的執行上下文彈出棧。
  • 當分配的調用棧空間被占滿時,會引發“堆棧溢出”問題。

所以,斐波那契數列函數優化的手段就是使用循環來減少函數調用,從而減少函數執行上下文的創建壓入棧的情況,就可以解決棧溢出的報錯了。(遞歸尾部優化無法解決問題,Chrome瀏覽器還是棧溢出),使用蹦床函數來解決:

function runStack (n) {
  if (n === 0) return 100;
  return runStack.bind(null, n- 2); // 返回自身的一個版本
}
// 蹦床函數,避免遞歸
function trampoline(f) {
  while (f && f instanceof Function) {
    f = f();
  }
  return f;
}
trampoline(runStack(1000000))
瀏覽器中 JavaScript 的執行機制

 

可以看到,調用棧中一直是保持3個執行上下文而已,多余的都及時的pop掉了。

作用域鏈

每個執行上下文的變量環境中,都包含了一個外部引用,用來指向外部的執行上下文,我們把這個外部的引用稱為 outer

當一段代碼使用一個變量是,JavaScript 引擎首先會在“當前的執行上下文”中查找該變量,如果找不到就會繼續在 outer 所指向的執行上下文中查找。我們把這個查找的鏈條就稱為作用域鏈

瀏覽器中 JavaScript 的執行機制

 

詞法作用域

詞法作用域就是指作用域是由代碼中函數聲明的位置來決定的,所以詞法作用域是靜態的作用域,通過它就能夠預測代碼在執行過程中如何查找標識符。詞法作用域是代碼階段決定好的,和函數是怎么調用的沒有關系。

瀏覽器中 JavaScript 的執行機制

 

塊級作用域中的變量查找

  • 從當前執行上下文的詞法環境,自頂向下查找(棧中的內存塊),然后再從當前執行向下文中的變量環境中查找;
  • 查找不到,則繼續在outer指向的執行上下文繼續依次先從詞法環境,再到變量環境查找。

閉包

有詞法作用域的規則可以知道,內部函數總是可以訪問他們的外部函數中的變量,當外部函數執行完畢后,pop stack了,遺留下了外部環境形成的閉包 Closure 環境,該環境內存中還保存著那些可以訪問的變量,類似一個專屬背包,除了內部函數訪問,氣氛方式無法訪問該專屬背包,我們就包這個背包稱為外部函數的閉包(那些內部函數引用外部函數的變量依然保存在內存中,我們把這些變量的集合稱為閉包)。

閉包是怎么回收的

如果引用閉包的函數是一個全局變量,那么閉包會一直存在知道頁面關閉;如果這個閉包以后不再使用的話,就會造成內存泄漏。

如果引用閉包的函數是一個局部變量,等函數銷毀后,下次 JavaScript 引擎執行垃圾回收時,判斷閉包這塊內容如果不再被使用了,那么 JavaScript 引擎的垃圾回收器就會回收這塊的內存。

使用閉包的原則:如果閉包會一直使用,那么它可以作為全局變量而存在;但如果使用頻率不高,而且占用內存有比較大的話,那就盡量讓它成為一個局部變量。

this

let a = { name: 'this解釋' }
function foo() {
  console.log(this.name)
}
foo.bind(a)() // => 'this解釋''

 

瀏覽器中 JavaScript 的執行機制

 

 

參考資源:《瀏覽器的工作原理與實踐》極客時間-李兵

分享到:
標簽:JavaScript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定