Debug JavsScript的时候最简单直觉的方式就是console.log,不过除了log以外,Console API其实还有非常多好用的Method,来看看Console家族中有哪些厉害的成员吧。
Console API
大部分的时候console.log都能解决问题,不过用对Method可以省去更多时间。
console.assert
它的作用和console.log差不多,最大的差别是当第一个参数是falsy时才会作用。
[false, null, undefined, 0, -0, 0n, NaN, ""]
为了检查user物件的name属性有没有问题,可能会写出以下的程式码来Debug:
const user = {
name: ""
};
if (!user.name) {
console.log('哪邊出錯了QQ', user);
}
这种状况使用console.assert就不需要另外加入if判断式,不但写了更少程式码,在语意上也很清晰:「条件不符就抛错。」:
console.assert(user.name, '哪邊出錯了QQ', user);
唯一要注意的就是第一个参数必须是falsy值才会出现error,条件写反了什么事情都不会发生。
console.count
console.count(label)会印出这个标签被执行了几次,预设值是default,可以用在快速的计数。
可以用以下的程式码试试console.count的效果:
function count(arg) {
console.count(arg);
}
count('foo');
count('bar');
count('bar');
也能用来检查多种状况的出现次数:
for (let i = 0; i < 5; i++) {
const int = Math.ceil(Math.random() * 100);
if (int < 20) console.count('太高了');
if (int > 20) console.count('太低了');
}
console.countReset
与count相生,用来归零,可用在计算单次行为的触发的计数,例如想在React中计算按下按钮后总共触发了几次Render。
同步的状况下Event handler内的setState会被React Batch在一起,但非同步时每个setState都会触发Render,因此以下范例在点击按钮后会触发3次Render。
function App() {
const [count, setCount] = React.useState(0);
const [count2, setCount2] = React.useState(0);
const [asyncCount, setAsyncCount] = React.useState(0);
const [asyncCount2, setAsyncCount2] = React.useState(0);
const onClick = () => {
console.countReset('render'); // 計算前先把 'render' 歸零
setCount(count + 1); // 1
setCount2(count2 + 1); // 1
Promise.resolve().then(() => {
setAsyncCount(asyncCount + 1); // 2
setAsyncCount2(asyncCount2 + 1); // 3
})
}
console.count('render');
return (
<div onClick={
onClick}>
<h1>Hello, please click me.</h1>
<h2>{
count}</h2>
</div>
);
}
console.group
为了在一大堆混乱的讯息中一眼看到自己的log,是否曾经写出这样的程式码呢?
console.log('---------');
console.log(object);
console.log('---end---');
虽然—是很显眼没错,但其实有更好的做法,用console.group可以自订Message group的标签也可以多层嵌套,并用console.groupEnd来关闭Group:
console.group('Start debugging');
console.log('de-');
console.group('Nested');
console.warn('deeper message');
console.groupEnd();
console.log('bug');
console.groupEnd();
另外还有Group的兄弟console.groupCollapsed,只差在预设Gourp是闭合的需要手动展开。
console.table
如果在这些Console API中只能选一个来介绍,那肯定是console.table,笔者自己就常常用到它。
需要印出阵列中的物件时,比起直接用console.log印出再慢慢展开,console.table绝对是更好的选择,来看看以下范例。
const rows = [
{
"name": "Frozen yoghurt",
"calories": 159,
"fat": 6,
"carbs": 24,
"protein": 4
},
{
"name": "Ice cream sandwich",
"calories": 237,
"fat": 9,
"carbs": 37,
"protein": 4.3
},
{
"name": "Eclair",
"calories": 262,
"fat": 16,
"carbs": 24,
"protein": 6
}
];
直接执行console.log(rows)会发生什么事情呢?
这绝对不会是Debug时想要看到的东西,需要手动展开物件才能看到内容,如果按住Option或Alt来一次展开全部属性呢?
只能展开第一个物件,而且把__proto__都给展开了,试试 console.table(rows)会印出什么结果:
相较console.log直接印出物件本身,console.table会以表格来印出物件内容,一次显示更多资讯,另外可以用参数改变显示的栏位以及拖拉调整栏位的宽度。
console.table(rows, [‘name’, ‘fat’]);
除了显示上更为清楚外,console.table还解决了另一个问题,试试在Console中执行以下代码,首先宣告一个物件animal,以console.log印出后再修改物件的属性:
const animal = {
name: 'mimi',
type: 'cat',
other: {
emoji: '?',
sound: 'meow'
}
};
console.log(animal);
animal.name = 'ami';
执行后会再手动展开物件出现以下结果:
可以看到展开前后的name属性值是不同的,由于执行console.log的当下还没修改animal的内容,正确显示了执行 console.log当下的物件值,也是一般预期想看到的值,但手动展开物件的时候值已经改变了,而使用console.table的话正好能避开这种困惑的情况。
不过这样的行为其实不是个问题,执行console.log时右上角会有一个小图示,Hover上去会看到提示Value below was evaluated just now.,说明了看到的是展开当下物件的值。
同场佳映:当物件内容较深的时候,JSON.stringify(animal, null, 2)也是不错的选择,直接将物件转为JSON字串全部显示。
console.time
想要测量如使用者行为或是Function 执行的时间的话,很常看到一种方式-- 算数学:
const t0 = performance.now();
alert('Hello World!');
const t1 = performance.now();
alert('Another Hello World!');
console.log(`Spent: ${
t1 - t0} ms`);
const t2 = performance.now();
console.log(`Spent: ${
t2 - t0} ms`);
想要快速测试时间还写了这堆程式码实在有点恼人,用console.time来改写一下,和console.group一样可以传入标签参数来识别计时器:
console.time('Spent');
alert('Hello World!');
console.timeLog('Spent');
alert('Another Hello World!');
console.timeEnd('Spent');
用法非常简单,用time启动计时器后可用无限个timeLog来印出目前过了多久时间,最后用timeEnd来停止计时器,如果在timeLog或timeEnd中放入未启动的标签会喷Warning。
console.trace
如果出问题的部分和其他套件有关系,尤其是一个Function会在多处被使用的时候,有别于console.log只能得知执行当下程式码的位置,console.trace会印出Call stack并直接展开,能更快速看出问题:
function a() {
console.trace();
}
function b() {
a();
}
function c() {
b();
}
b()
c()
笔者曾经在使用影片播放器套件的时候,不知道是不是自己写坏了,有时影片会突然暂停,那时就用了类似以下的程式码来检查,马上就看出是套件中某个Function触发了暂停。
video.addEventListener('pause', console.trace);
小结
今天讲解的Console API 有许多个都是笔者爱用的Debug 方式,确实节省了不少时间呢,明天将会讲解可在Console 面板中使用,但不属于Console API 的内建Debug Function。