小tips:在JS語句執行機制涉及的一種基礎類型Completion


看一個如下的例子。在函數 foo 中,使用了一組 try 語句。在 try 中有 return 語句,finally 中的內容還會執行嗎?

function foo(){
  try{
    return 0;
  } catch(err) {

  } finally {
    console.log("a")
  }
}

得到的結果是:a 0。

根據結果可以看出finally執行了,return語句也生效了。

雖然 return 執行了,但是函數並沒有立即返回,又執行了 finally 里面的內容,這樣的行為違背了很多人的直覺。

我們改一下上面的例子,在finally也加入return語句,如下:

function foo(){
  try{
    return 0;
  } catch(err) {

  } finally {
    return 1;
  }
}

得到的結果是:1

通過實際執行,我們看到,finally 中的 return “覆蓋”了 try 中的 return。在一個函數中執行了兩次 return,這已經超出了很多人的常識,也是其它語言中不會出現的一種行為。

面對如此怪異的行為,我們當然可以把它作為一個孤立的知識去記憶,但是實際上,這背后有一套機制在運作。

這一機制的基礎正是 JavaScript 語句執行的完成狀態,我們用一個標准類型來表示:Completion Record(Completion Record 用於描述異常、跳出等語句執行過程)。

Completion Record 表示一個語句執行完之后的結果,它有三個字段:

  • [[type]] 表示完成的類型,有 break continue return throw 和 normal 幾種類型;
  • [[value]] 表示語句的返回值,如果語句沒有,則是 empty;
  • [[target]] 表示語句的目標,通常是一個 JavaScript 標簽。

JavaScript 正是依靠語句的 Completion Record 類型,方才可以在語句的復雜嵌套結構中,實現各種控制。接下來我們要來了解一下 JavaScript 使用 Completion Record 類型,控制語句執行的過程。

語句的分類如下:

 普通語句執行后,會得到 [[type]] 為 normal 的 Completion Record,JavaScript 引擎遇到這樣的 Completion Record,會繼續執行下一條語句。

這些語句中,只有表達式語句會產生 [[value]],當然,從引擎控制的角度,這個 value 並沒有什么用處。

如果你經常使用 chrome 自帶的調試工具,可以知道,輸入一個表達式,在控制台可以得到結果,但是在前面加上 var,就變成了 undefined。

Chrome 控制台顯示的正是語句的 Completion Record 的 [[value]]。

現在解釋下在finally也加入return語句后,為什么得到的結果為1?

因為 finally 中的內容必須保證執行,所以 try/catch 執行完畢,即使得到的結果是非 normal 型的完成記錄,也必須要執行 finally。

而當 finally 執行也得到了非 normal 記錄,則會使 finally 中的記錄作為整個 try 結構的結果。

帶標簽的語句的作用:與完成記錄類型中的 target 相配合,用於跳出多層循環。

實際上,任何 JavaScript 語句是可以加標簽的,在語句前加冒號即可:

firstStatement: var i = 1;

跳出循環的例子:

top:
  for (var i = 0; i < 3; i++){
    for (var j = 0; j < 3; j++){
      if (i === 1 && j === 1) break top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM