不是 Console API 的 Console 工具#
除了 Console API(console.log、console.table 等可以寫在程式碼裡的 method)之外,DevTools 還在 Console panel 內提供了一組 Debug 專用的 utility function。這些是 Console Utilities API,每一個都身懷絕技。
這些 utility 只在 Console panel 內存在,把它們寫進實際的程式碼會直接拋
Uncaught ReferenceError: xxx is not defined。原作者第一次學到這些功能太興奮、寫進程式碼裡才發現這件事。
$_#
$_ 儲存了 Console 上一次執行的結果。在 Console 試 JavaScript 行為時通常都是逐步驗證,這時 $_ 非常順手。
[1, 2, 3, 4].map((x) => x * 2);
// $_ 現在是 [2, 4, 6, 8]
$_.reduce((a, b) => a + b);
// 20
當需要串接很多不能用 method chaining 的 function 時,可以利用 $_ 避免游標來回修改:每次只動最後一行,不會破壞前面打好的程式碼。
未來 JavaScript 也許會有 Pipeline operator(TC39 提案)做到任意 function chaining,提升可讀性、不需要修改內建 prototype。
let a; a = 1 |> ((n) => add(n, 5)) |> double; console.log(a); // 12
$, $$#
$(selector[, element]):等於document.querySelector$$(selector[, element]):等於document.querySelectorAll
這兩個語法借自 jQuery,是 Console 內部寫死的 alias。
第二個參數可以指定起始元素,搭配 $0 就能「先 Inspect 一個元素,再從它底下搜尋」:
$(".btn", $0);原作者常用 $$ 在 Console 快速測一些行為,例如印出個人 GitHub 頁面的所有 repository 名稱:
$$("h3 a").map((a) => a.textContent.trim());如果頁面已經引入 jQuery,
$會是 jQuery 本身,而不是 DevTools 的 alias。
$x#
$x(xpath[, element]) 用 XPath 查詢元素,回傳陣列。當 CSS 選取器(Selector)無法表達某些查詢條件(例如「找到內文是某段文字的元素」)時很有用。
$x("//a[contains(text(), 'Login')]");debug / undebug#
debug(function) 把指定 function 標記成「執行時自動觸發 debugger」:
function a() {
console.log(1);
}
debug(a);
// undebug(a); // 取消
效果等同於:
function a() {
console.log(1);
}
a = (function () {
const origin = a;
return function () {
debugger;
origin();
};
})();關於斷點(Breakpoint)的詳細用法,會在 Sources panel 的章節展開。
monitor / unmonitor#
monitor(function) 跟 debug 用法類似,差別在:被 monitor 的 function 執行時會印出 function 名稱與實際傳入的參數,但不會中斷執行。
function greet(name) {
return `Hello, ${name}`;
}
monitor(greet);
greet("Andrew");
// function greet called with arguments: Andrew
用 unmonitor(function) 停止監控。
monitor不能用在 arrow function。要監控 arrow function,只能手動覆寫。
monitorEvents#
monitorEvents(element[, eventType]) 監聽元素的事件並印出來。可以聽單一事件,也能聽一整類事件:
monitorEvents(window, "click");
monitorEvents(window, "touch"); // 一次聽 touchstart, touchmove, touchend, touchcancel
第二個例子等效於:
window.addEventListener("click", console.log);
window.addEventListener("touchstart", console.log);
window.addEventListener("touchmove", console.log);
window.addEventListener("touchend", console.log);
window.addEventListener("touchcancel", console.log);用 unmonitorEvents(element[, eventType]) 停止。
getEventListeners#
getEventListeners(element) 印出已註冊在該元素上的事件監聽器。
延續上面的例子,先 monitorEvents(element) 再 getEventListeners(element),會看到所有事件都被註冊了一輪。
展開後可以看到每個 listener 的屬性:
listener:實際被觸發的 functiononce:是否為一次性監聽passive:是否不能呼叫event.preventDefault(),常用來提升scroll等高頻事件的效能type:監聽的事件類型useCapture:是否在 capture 階段攔截事件
這些屬性都是 addEventListener 的選項。
const options = {
capture: true,
passive: true,
once: false,
};
window.addEventListener("click", console.log, options);
// window.removeEventListener('click', console.log, options);
移除 listener 時必須提供「相同的」options,否則
removeEventListener不會生效。
queryObjects#
queryObjects(constructor) 官方說明是「列出由該 constructor 產生的所有 instance」,但更精準的說法是:印出所有「prototype 鍊上包含該 prototype」的物件。
class A {}
const a = new A();
const b = Object.create(a);
queryObjects(A);
// 會印出 a 與 b
queryObjects 不是 return 陣列、而是延遲列印結果,要把結果存起來操作可以用「右鍵 ➡️ Store as global variable」,會自動存到 temp1 等變數。
copy#
copy(object) 把 DOM 元素或物件複製到剪貼簿。
實用情境:
- 把物件轉成 JSON 貼到對話框討論 API spec
- 在 Console 快速建立、修改假資料後直接複製出去
複製物件時 DevTools 會自動加好縮排,不需要再手動 prettier 一次。
keys, values#
keys(object)≈Object.keys(object)values(object)≈Object.values(object)
只回傳物件「自身」的可枚舉屬性。為什麼要強調自身?因為 for...in 會把 prototype 鍊上的所有可枚舉屬性都跑一遍:
const object = Object.create({ foo: 1 });
object.bar = 2;
for (let key in object) {
console.log(key);
}
// bar
// foo
要過濾「只看自身屬性」,正確寫法是:
for (let key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
console.log(key);
}
}
// bar
為什麼用 Object.prototype.hasOwnProperty.call(object, key) 而不是 object.hasOwnProperty(key)?看下面例子:
const object1 = {
hasOwnProperty: function () {
return false;
},
};
const object2 = Object.create(null);
object1.key = "key";
object2.key = "key";
object1.hasOwnProperty("key"); // false(被覆寫)
object2.hasOwnProperty("key"); // TypeError(沒有繼承 Object.prototype)
object.hasOwnProperty 可能被覆寫、或根本不存在。
clear#
clear() 清空 Console panel。雖然左上角的 ? 圖示也能清,但用 clear() 像在 terminal 輸入 clear 一樣順手。
在
Preserve log開啟的情況下,clear()不會清空 Console。這是設計上的權衡:保留 log 的優先度高於 clear。
銜接#
Console Utilities API 走完了。下一章是 Console 系列的最後一章,講「在 Console 內執行 JavaScript」的小撇步:換行縮排、Default Async、Context 切換、Live Expression。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10241598