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

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

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

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

獲得的結果是:a 0。函數

根據結果能夠看出finally執行了,return語句也生效了。工具

雖然 return 執行了,可是函數並無當即返回,又執行了 finally 裏面的內容,這樣的行爲違背了不少人的直覺。spa

咱們改一下上面的例子,在finally也加入return語句,以下:調試

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

  } finally {
    return 1;
  }
}

獲得的結果是:1code

經過實際執行,咱們看到,finally 中的 return 「覆蓋」了 try 中的 return。在一個函數中執行了兩次 return,這已經超出了不少人的常識,也是其它語言中不會出現的一種行爲。orm

面對如此怪異的行爲,咱們固然能夠把它做爲一個孤立的知識去記憶,可是實際上,這背後有一套機制在運做。blog

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

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

  • [[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
相關文章
相關標籤/搜索