概覽#

這是 Console 系列的最後一章,重點放在「在 Console panel 裡執行 JavaScript」的細節:換行縮排、Autocomplete、Default Async、Context 切換、Live Expression。這些功能都不大,但組合起來會把 Console 從「印 log 的地方」升級成「真正能拿來工作的 REPL」。

換行與縮排#

剛開始在 Console 輸入多行程式碼很不順:要不是手動縮排,要不就是還沒打完不小心按 Enter 直接執行噴錯。但 Console 其實比想像中聰明,搞懂規則後輸入效率會大幅提升。

預設行為:

  • Enter:執行
  • Shift+Enter:換行

但 Console 會根據語法判斷該不該執行:

  • 如果輸入了一個 statement,或剛打完 {,按 Enter 會「自動換行」而不是執行
  • 一直到 statement 結束、或遇到對應的 },按 Enter 才會真正執行
  • 前提:游標必須在 Console 的「最後一行」

換句話說,輸入 if (...) 後 Enter,Console 會自動進入續行模式,直到 } 配對結束。

如果要編輯中間的某段已輸入內容,建議先把後半段剪下,編輯完前面再貼回來,避免游標不在最後一行而觸發執行。

statement 可以簡單理解為「不會回傳值」的程式碼,例如:

if (condition) {
  ...
}

for (let i = 0; i < 100; i++) {
  ...
}

function test() {
  ...
}

Rerun 與 Autocomplete#

輸入程式碼時跳出的下拉選單包含兩種來源:

  • 目前 context 中可用的變數
  • 曾經輸入過的歷史指令(前面有 > 標示)

按方向鍵上下也可以瀏覽過去輸入過的程式碼,跟 terminal history 一樣。

如果歷史紀錄太雜、不想出現在 autocomplete 裡,有兩個處理方式:

  • Console settings 取消 Autocomplete from history
  • 在 Console 內按右鍵 ➡️ Clear console history

另外有個小細節:輸入 JavaScript 內建函式時打到 (,DevTools 會跳出簡單的參數提示,不用每次都試誤或開 MDN。

Default Async#

如果用過 async / await 語法,應該知道 await 只能在 async function 裡面用。但 ⋯

在 Console 內可以「直接使用」await,不需要外層包 async function。

以前要用 await 必須包一個立即函式(IIFE):

(async () => {
  console.log("zzz...");
  await sleep(1000);
  console.log("Wake up!!");
})();

現在直接這樣寫就行:

console.log("zzz...");
await sleep(1000);
console.log("Wake up!!");

對於試 fetch、Promise、各種非同步 API,省下大量樣板程式碼。

Context#

網頁中可能同時存在多個 context,常見的有:

  • 主 frame(top frame)
  • iframe
  • 瀏覽器 Extension 注入的 script
  • Service Worker

Console panel 上方的 Context 選單可以列出所有可用 context,點選後就會切到該 context 執行 JavaScript。這就能做到:操作 iframe 內的元素、和 Extension 或 Service Worker 互動等等。

預設情況下,top frame 沒辦法直接存取 iframe 內定義的變數(被 same-origin policy 保護)。要 debug iframe 內的東西,必須切到該 iframe 的 context。

打開 Context 選單時,hover 到某個 iframe context,頁面上對應的 iframe 區塊會亮起來,方便辨識來源。

Live Expression#

Console 上方有一個眼睛圖示,這是 Live Expression。需要持續觀察某個變數值如何變化時,按下圖示新增一組表達式,那個值就會被釘在 Console 最上方即時更新,省得不停手動執行。

但要注意:Live Expression 「會」觸發副作用。它的機制比較像 setInterval,以特定頻率重新執行表達式並更新顯示,跟前一章提到的 Eager Evaluation 不一樣(Eager Evaluation 偵測到副作用就會跳過預覽)。

舉個經典例子,怎麼讓 a == 1 && a == 2 同時為 true:

const a = {
  i: 1,
  toString() {
    return this.i++;
  },
};

這個物件的 toString 每次被呼叫都會把 i 自增。在 Console 直接輸入 a == 1 時,因為 Eager Evaluation 偵測到副作用,不會出現預覽(嚴格模式(Strict Mode)下也不會);但 a === 1 沒有 type coercion,不會呼叫 toString,所以會有預覽。

如果把 a == 1 加進 Live Expression,就會看到 a.i 的值不斷被往上加,因為 Live Expression 會持續呼叫表達式 ➡️ 持續呼叫 toString ➡️ 持續 this.i++

用 Live Expression 觀察值時要注意它對表達式的副作用,避免「看的動作改變了被看的東西」。

銜接#

Console 系列到此告一段落。接下來的章節進入 Sources panel:JavaScript Debugging 的重頭戲,包含 Source Map、Workspace、Breakpoint、Step-by-Step 執行等等。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10242319