除錯第一步:讓強大的console家族助你一臂之力

愣錘發表於2018-09-26

console相比大家一定不陌生,平時專案中用的最多的就是console.log()方法吧。但是console相關的方法有很多,涉及的除錯皮膚的相關內容比較廣泛,徹底弄清楚它們並在專案中合理使用,有助於我們更好的開發和除錯。

下面我們在控制檯列印一下console,看看它還有哪些神奇的方法:

除錯第一步:讓強大的console家族助你一臂之力

如果沒了解過console的,似不似驚呆了,console還有這麼多方法?下面我們從最簡單的console.log方法開始,逐個分析其他方法以及所涉及到的除錯技巧。

1.console.log()列印內容。這個方法那是太熟悉不過了,平時也是用的最多的或許也是這一個吧!!!基本用法呢就不說了,主要聊一聊console.log()的佔位符。其共有五種佔位符,分別是:

  1. %s 字串
  2. %d 或 %i 整數
  3. %f 浮點數
  4. %o 物件的連結
  5. %c CSS格式字串

如果方法的第一個引數中使用了佔位符,那麼就依次使用後面的引數進行替換。

const name = 'chinaBerg';
const age = 88;
const money = 12.88;
const obj = {
    status: '很積極'
}

console.log('我叫%s,%d歲,有%f元,狀態:%o', name, age, money, obj.status, '又列印一句話')複製程式碼

谷歌列印結果:

除錯第一步:讓強大的console家族助你一臂之力

可以看到我們後面使用的引數對前面的佔位符進行了替換,有點像我們字串拼接的簡化操作。比如我們es5中的字串拼接:

console.log('我叫' +  name + ' ,' + age +'歲,有' + money + '元')複製程式碼

當然了,es6已經有了更強悍的字串模板:

console.log(`我叫${name}${age}歲, 有${money}元`);複製程式碼

拼接的字串中混著使用也是可以的:

//例如,這裡演示的:第一引數是拼接的字串,第二引數插入字串的浮點數
console.log('我叫' +  name + ' ,' + age +'歲,有%f元',  money)複製程式碼

但es6的字串模板中,只能使用%c佔位符,其他佔位符是沒有效果的。

// 注意這裡字串模板的最後插入了%f
console.log(`我叫${name}${age}歲, 有%f元`, 12.88);複製程式碼

除錯第一步:讓強大的console家族助你一臂之力

%c佔位符還是略有趣味的:

const css1 = 'font-size: 22px;font-weight: bold';
const css2 = 'border: 1px solid green';
const css3 = 'color: #fff;background: #f00';

// 佔位符填入				
console.log('%c %s + %s = %s', css1, 1, 2, 3);
// 字串拼接形式中插入%c佔位符
console.log('%c我的名字叫' + name + ', 今年' + age + '歲', css2);
// es6字串模板中插入%c佔位符
console.log(`%c我叫${name}${age}歲, 有%f元`, css3);
複製程式碼

谷歌列印效果:

除錯第一步:讓強大的console家族助你一臂之力

可以看到這些列印的內容已經被新增了我們的樣式。

---------------------------------------------------------------------

2.但是和console.log()很像的還有倆,一個是console.info(),一個是console.debug()。其實這個三個功能都是一樣的,只不過有些區別,下面就具體介紹一下這三個方法。

先來看一下下面這三行程式碼,在谷歌、火狐、ie上的列印效果:

console.log('我是console.log()列印出來的');

console.info('我是console.info()列印出來的');

console.debug('console.debug()列印出來的')複製程式碼
谷歌瀏覽器控制檯列印效果:

除錯第一步:讓強大的console家族助你一臂之力

火狐控制檯:

除錯第一步:讓強大的console家族助你一臂之力

ie控制檯:

除錯第一步:讓強大的console家族助你一臂之力

從結果可以看出:

  • console.log()方法,無論哪個瀏覽器,列印出的效果都是一樣的。
  • console.info()方法,ie沒有列印出來,即不支援這個屬性。但是在谷歌和火狐上又略有區別:列印的結果是一樣的,但是火狐控制檯上,會在列印的結果前面新增一個類似i的小符號。
  • console.debug()方法,谷歌和opera是不支援的,ie和火狐是支援的。

所以呢,既然三個方法功能是基本一樣的,我們如果只想列印一些內容的話,還是老老實實的使用console.log()穩一點。當然了,這也抵不住你就使用火狐來debug呢!可是,考慮到如果你的程式碼庫的某些列印資訊需要給別的開發者看的話,還是用相容性更好的穩一些。

---------------------------------------------------------------------

3.console.clear() 清除控制檯列印的內容,並將游標迴歸到第一行。

除錯第一步:讓強大的console家族助你一臂之力

這個屬性沒什麼好說的,和我們點選控制檯的這個清除按鈕的效果一樣。

---------------------------------------------------------------------

4. console.assert(表示式 [,arg1, arg2……argn])列印斷言。

第一個引數是用來判斷是否列印斷言的表示式,只有當表示式的值為falsy的時候,才會列印後續的引數:

const arr = [1, 2, 3];

// 列印斷言,如果arr[0]的值不等於2,則列印提示資訊
console.assert(arr[0] === 2, 'arr[0]的值不等於2');複製程式碼

谷歌控制檯列印如下:

除錯第一步:讓強大的console家族助你一臂之力

如果沒有引數,預設列印如下字串:

除錯第一步:讓強大的console家族助你一臂之力

其他注意點:
  • 客戶端的console.assert()列印斷言,並不會阻塞後續程式碼的執行,只是在斷言的表示式為false的時候,向控制檯列印你的內容。
  • 而在node.js中,值為假的斷言將會導致一個AssertionError被丟擲,使得程式碼執行被打斷。這兩者是有區別的。

---------------------------------------------------------------------

5.console.count() 列印計數。輸出他被呼叫了多少次。

傳遞一個引數作為計數提示:

for (let i = 0; i < 10; i++) {
    console.count('我被呼叫了');
}複製程式碼

谷歌控制檯:

除錯第一步:讓強大的console家族助你一臂之力

簡單修改一下:

for (let i = 0; i < 10; i++) {
    console.count(`我是${i}我被呼叫了`);
}複製程式碼

列印效果:

除錯第一步:讓強大的console家族助你一臂之力

這個方法意思就是:向控制檯寫入在同一行使用相同標籤呼叫 count() 的次數。

就是如果你給count()傳遞的引數值不一樣,那麼是分開計數的。

再看一個簡單的示例:

function fun (name) {
    console.count(name)
}
fun('小米');
fun('小剛');
fun('小米');複製程式碼

效果:

除錯第一步:讓強大的console家族助你一臂之力

所以,即便同一個函式,我們可以清晰的知道小米同學被呼叫了2次,小剛同學被呼叫了1次。

如果不傳遞引數,預設的計數提示標籤是default字串:

for (let i = 0; i < 10; i++) {
    // count()沒傳遞提示標籤
    console.count();
}複製程式碼

效果如下:

除錯第一步:讓強大的console家族助你一臂之力

一般在某些迴圈中,如果我們想知道一個函式或者變數被執行或者呼叫了多少次的時候,可以使用console.count()方法,而通過傳遞提示標籤,我們更可以清晰的知道一個函式分別被不同的情況呼叫了幾次,從而幫助我們定位錯誤資訊。

---------------------------------------------------------------------

6.console.time()和console.timeEnd()列印計時。用來跟蹤某一個操作的佔用時長。每一個計時器必須擁有唯一的名字,time()的引數名和timeEnd()的引數名要一樣。可以沒有引數,預設計時提示為default:

// 立即啟動計時器
console.time()

// 某些操作
for (let i = 0; i < 10000; i++) {
    // 某些操作				
}

// 立即結束計時器,並輸出計時結果
console.timeEnd()複製程式碼

控制檯列印效果如下:

除錯第一步:讓強大的console家族助你一臂之力

傳遞計時器提示:

// 立即啟動計時器
console.time('time')

// 某些操作
for (let i = 0; i < 10000; i++) {
    // 某些操作				
}

// 立即結束計時器,並輸出計時結果
console.timeEnd('time')複製程式碼

控制檯列印效果如下:

除錯第一步:讓強大的console家族助你一臂之力

注意:

  • 頁面中最多能同時執行10,000個計時器
  • 該方法並不會將結算結果返回到js中,而只是能列印在控制檯上。所以不能使用此方法在js中來作為普通計時器使用或者進行效能收集器的一部分。


7.console.dir() 輸出以 JavaScript 形式表示的指定物件。如果正在記錄的物件是 HTML 元素,將輸出其以 DOM 形式表示的屬性。

列印一個物件:

// 一個物件
const obj = {
    name: '某某渣',
    age: 22,
    sex: '男'
}

// dir列印
console.dir(obj);

// log列印
console.log(obj);複製程式碼

谷歌控制檯效果:

除錯第一步:讓強大的console家族助你一臂之力

對於物件或者json等,console.log()和console.dir()效果基本一樣。

但是如果列印的是一個dom元素:

// dir列印
console.dir(document.body);

// log列印
console.log(document.body)複製程式碼
  • console.dir()會將dom的所有屬性和事件都被列印出來:除錯第一步:讓強大的console家族助你一臂之力
  • console.log()列印的就是dom:除錯第一步:讓強大的console家族助你一臂之力

如果哪天你coding的時候突然想不起來dom的某個方法了,你完全可以console.dir()一下,而不必去翻閱資料了。

---------------------------------------------------------------------

8.console.dirxml(object) 如果可以,輸出 object 子級元素的 XML 表示形式,否則輸出其 JavaScript 表示形式。 在 HTML 和 XML 元素上呼叫 console.dirxml() 等同於呼叫 console.log()。

---------------------------------------------------------------------

9.console.group() + console.groupEnd()將控制檯輸出的內容進行分組。

將列印的資訊歸類分組列印,並且可以展開、摺疊。這在輸出大量資料的或許有用。

// console.groupCollapsed() + console.groupEnd()的形式,預設是摺疊的
console.group('分第一組');
console.log('html')
console.dir({ type: '前端'}),
console.groupEnd('分第一組')

// console.group() + console.groupEnd() 預設是展開的
console.group('分第2組');
console.log('php')
console.dir({ type: '後臺'}),
console.groupEnd('分第2組')複製程式碼

谷歌列印效果:

除錯第一步:讓強大的console家族助你一臂之力

---------------------------------------------------------------------

10.console.table()可以將陣列、物件等複雜型別的資料列印成表格的形式。

列印簡單的陣列:

const arr = ['a', 'b'];
			
console.table(arr)複製程式碼

除錯第一步:讓強大的console家族助你一臂之力

列印複雜的陣列:

const arr = [
    {
        name: '小明',
        age: 22,
        likes: ['跳舞', '上網']
    },
    {
        name: '小剛',
        age: 23,
        likes: ['擼碼', '計算機']
    }
];
				
console.table(arr)複製程式碼

除錯第一步:讓強大的console家族助你一臂之力

列印物件:

const obj = {
    name: '小明',
    age: 22,
    likes: [
        {
            a: 1,
            b: 2
        },
        {
            a: 3,
            b: 4
        },
    ]
}
				
console.table(obj)複製程式碼

除錯第一步:讓強大的console家族助你一臂之力

通過console.table()列印出的結果,我們可以很直觀的看到資料的組成。

---------------------------------------------------------------------

11.console.trace()堆疊中呼叫此方法的路徑。

如果想要清楚地知道一個函式的呼叫軌跡,可以將此方法寫在函式內部,便可以跟蹤函式的呼叫軌跡,程式碼實現如下:

function test(name) {
    console.trace(`此處呼叫了${name}`)
}
				
function doSome (name) {
    test(name);
}
				
doSome('翠花');複製程式碼

谷歌控制檯列印如下:

除錯第一步:讓強大的console家族助你一臂之力

此處列印出了js中呼叫test()的所有堆疊位置。從上到下依次為最裡層的呼叫一直到最外層呼叫。平時我們使用第三方庫的時候,如果寫法不對,經常可以在控制檯看到我們的報錯資訊,並且像這樣列印出了錯誤位置的堆疊資訊。

---------------------------------------------------------------------

12.console.warn()列印一條警告資訊

console.warn('我是一條警告')複製程式碼

谷歌列印結果如下:

除錯第一步:讓強大的console家族助你一臂之力

列印結果會有一條黃色背景,前面附加一個感嘆號的圖示。

預設是收起的,點選可以展開,列出了警告位置的堆疊資訊,點選堆疊位置可以對應開啟警告位置程式碼。這個的使用沒什麼好說的,如果你需要列印一條警告資訊,用這個方法很合適。

---------------------------------------------------------------------

13.console.error()列印錯誤。

console.error('我這裡出現了錯誤,我來告知使用者')
複製程式碼

谷歌列印結果如下:

除錯第一步:讓強大的console家族助你一臂之力

該方法主要用來列印錯誤,列印結果的樣式如上圖。也沒什麼好說的,不過如果你開發第三方庫的時候,可以用到。但是throw丟擲錯誤的方式也會用到不少。

---------------------------------------------------------------------

14.console.profile() 和 console.profileEnd() 新建一個效能分析器(基於cpu的使用情況)。用於函式效能分析的利器。

我們已經知道通過console.time()和console.timeEnd()我們可以知道一段程式碼的執行時間。但是,如果我們需要分析較為複雜的js邏輯程式碼,繼而從中找出程式執行的效能瓶頸的話,如果繼續使用console.time()方法的話,意味著我們要插入大量的該方法,這顯然是笨拙的,也是讓我們不可接受的。

相對於複雜邏輯的JavaScript程式調優,console.profile() 和 console.profileEnd()新建效能分析器便派上用場了。

用法和time的一樣,console.profile()開始,console.profileEnd()結束,需要傳遞一個引數作為標籤使用,說俗了點就是為這個效能分析器起個名字。看下如下程式碼,我們測試幾種不同for迴圈書寫方式的耗時情況:

// 簡單新建一個陣列吧,新建一個一千萬個成員為1的陣列
let arr = new Array(10000000).fill(1);
				
// 第一種for迴圈書寫方式				
function fun1 () {
    for (let i = 0, len = arr.length; i < len; i++) {}
}

// 第二種for迴圈書寫方式				
function fun2 () {
    for (let i = arr.length; i --; ) {}
    fun1();
}

// 第三種for迴圈書寫方式		
function fun3 () {
    for (let i = 0, item; item = arr[i++]; ) {}
}

// 執行三個函式		
function fun () {
    fun1();
    fun2();
    fun3();
}

// 立即開始一個效能分析器
console.profile('測試for迴圈');
fun();
//
console.profileEnd('測試for迴圈');
複製程式碼

執行如上程式,開啟谷歌控制檯一看:

除錯第一步:讓強大的console家族助你一臂之力

嗯,沒錯,列印了兩句話,效能分析器開啟和結束。納尼~說的效能分析器呢???小拳拳要捶你胸口了!!!

別急,效能分析器不在這裡,在javascript Profiler皮膚中。

除錯第一步:讓強大的console家族助你一臂之力

點選javascript Profiler皮膚,便可以看到效能分析器。如果你沒有上面紅框標識的皮膚,那麼點選右邊的三個點,在下拉選單中依次選擇More tools -> JavaScript Profiler選項,就可以將該選項新增到上面的紅框位置。然後點選該皮膚,進入對應內容:除錯第一步:讓強大的console家族助你一臂之力

可以看到,已經有了剛才的效能分析情況,這裡清晰展示了每一個函式執行過程所耗時間。然後我們點開每一個函式看下具體的情況:除錯第一步:讓強大的console家族助你一臂之力

圖上我進行了標註:

  • 1處,Self Time表示當前函式自身執行耗時,什麼意思?就是說當前函式自身執行耗時,不包括當前函式中呼叫的其他函式執行耗時。
  • 2處,Total Time表示當前函式執行總耗時,包括了自身執行耗時+函式內部呼叫的其他函式的執行耗時。
  • Function那一列,我們通過上圖開啟的fun1那一欄說明,fun1展開後的結果包括funfun2,這指的是函式fun1在函式funfun2中被呼叫執行的耗時。通過程式碼我們知道,fun1函式確實在fun函式和fun2個被呼叫過1次,所以這裡展示了fun在這兩處被呼叫執行的耗時時間。
  • 每個函式行最右邊還有會堆疊位置,點選即可進入resouce皮膚中該函式所在的檔案位置。

如果你關注fun1函式的執行時間,你可以點選選中fun1這一行,然後點選上面的眼睛圖示,將自動只為你展現fun1函式的資訊:

除錯第一步:讓強大的console家族助你一臂之力

  • 選中函式行,點選眼睛即針對性的展示當前函式。
  • 選中函式和,如果點選×號,將會刪除當前函式行。
  • 選中函式行點選眼睛進入後,如果想返回到上述全部函式行的皮膚,可以點選上圖重新整理按鈕。或者刪除了函式行後也可以恢復如上圖。
  • 上圖三個按鈕只有在顏色變深的時候才可點選,眼睛和×號只有在選中函式行的情況下可點,重新整理按鈕在進入或者刪除函式行之後可以點。

還有一點沒介紹,就是該這種資料展示方式,是預設的方式:Heavy(Bottom Up),即將所有執行的函式,按照耗時長度,從上到下降序排列,耗時的在最上面,不耗時的在最下面。但是他還有另外兩種方式(Chart 和 Tree),如下圖:

除錯第一步:讓強大的console家族助你一臂之力

我們先來說說Tree型資料分析,下面我們先切換到Tree型看下圖:除錯第一步:讓強大的console家族助你一臂之力

將每個函式行開啟後,顯示了該函式所呼叫的函式。這種資料分析的展示方式其實是,先展示最外層的函式,展開後,顯示該函式所呼叫的所有函式,依次往裡類推。每一行都展示該函式執行的耗時。其他操作同上。

最後說一下Chart的方式,Chart是以圖片給我們展示函式執行後效能,可以看到每個函式開始執行的時間節點,如下圖:除錯第一步:讓強大的console家族助你一臂之力

大致分為上下兩部分,上部分藍色區域為cpu佔用的大體走勢圖,可以清晰地看到每個時間節點的cpu佔用情況,是高是滴一目瞭然。下半部分為每個函式開始執行的時間節點。

如果點選上部分藍色區域,還可以更細粒度檢視當前事件的函式執行情況(在當前時間節點劃分為更細的力度),如下圖:

除錯第一步:讓強大的console家族助你一臂之力

滑鼠移入某個函式,還可以看到當前函式所執行的耗時情況,如下圖:

除錯第一步:讓強大的console家族助你一臂之力

console.profile() 和 console.profileEnd()函式效能分析器的建立,給我們分析函式效能帶來的非常大的便利,這對於我們檢測程式執行瓶頸非常有幫助。 谷歌為我們開發這麼好使的除錯工具,一定要在需要的時候好好利用。

---------------------------------------------------------------------

15.console.timeStamp('事件資訊'),在Performance(以前叫Timeline)效能皮膚中的會話錄製期間插入一條新增一個事件。

說到這個console.timeStamp()方法,這個方法在我們進行效能除錯的時候會用到。說到這個方法首先要提到Performance效能皮膚,因為該方法列印出來的結果需要在這個除錯皮膚中檢視,準確的來說,該方法是配合效能皮膚來除錯的:

除錯第一步:讓強大的console家族助你一臂之力

如上圖,在Perdormance皮膚中,我們可以分析當前頁面的效能,可以得知頁面載入和使用者互動相關的事件分析結果。關於Performance這塊的內容,如果仔細說起來,內容是比較多的。這裡暫且只介紹和console.timeStamp方法相關的內容。以後可以單獨把這塊拿出來細細分析和記錄。

迴歸正題,console.timeStamp可以在時間軸上寫入一個事件:

// 一些其他操作
for (let i = 0; i < 10000; i ++) {}

// 在錄製會話期間插入的第一個事件		
console.timeStamp('第一個迴圈完了')
				
// 一些其他操作
for (let i = 0; i < 10000; i ++) {}
				
// 在錄製會話起價插入的第二個事件
console.timeStamp('第2個迴圈完了')複製程式碼

錄製完會話後,我們輸入移入下圖紅框左上方的黃色豎線上可以看到彈出一個提示框,上面標註了Timestamp提示:‘第一個迴圈完了’,並且還有該事件插入時的時間節點。

除錯第一步:讓強大的console家族助你一臂之力


---------------------------------------------------------------------

16.console.markTimeline()方法效果等同於console.timeStamp(),是console.timeStamp()以前的寫法,已經淘汰了。不多說。

---------------------------------------------------------------------

17.console.timeLine('標籤引數')配合 console.timeLineEnd('標籤引數')錄製一段時間的時間軸。

我們在上面的console.timeStamp方法中瞭解到,在Performance皮膚中,我們可以錄製當前頁面的會話資訊,而通過console.timeline和console.timelineEnd可以只錄制某一段時間的會話資訊。

// 錄製第一段時間的會話資訊
console.timeline('測試迴圈100萬相關的效能分析')
for (let i = 0; i < 1000000; i ++) {}
console.timelineEnd('測試迴圈100萬相關的效能分析')


// 錄製第二段時間的會話資訊				
console.timeline('測試迴圈1000萬相關的效能分析')
for (let i = 0; i < 10000000; i ++) {}
console.timelineEnd('測試迴圈1000萬相關的效能分析')複製程式碼

在我們的Performance皮膚中,點選開始錄製當前頁面,然後檢視錄製後的結果:除錯第一步:讓強大的console家族助你一臂之力

除錯第一步:讓強大的console家族助你一臂之力

console.timeline('引數標籤')console.timelineEnd('引數標籤'),兩個方法需要接收相同的一個引數標籤,就是一個標識而已。

這裡會了這個用法之後,更多的是怎樣在Performance中進行效能的分析,然後找出影響程式效能的瓶頸,這才是重要的。


弄清楚了強大了console家族,可以在開發除錯過程中祝我們一臂之力。簡單的運用console相關內容,是除錯入門的第一步,更強大還在於谷歌的除錯皮膚的靈活合理使用,更高階的還有自動化測試。


如果覺得喜歡就❤❤❤一下吧!!!


相關文章