**摘要:** 玩转console。
- 原文:[灵活使用 console 让 js 调试更简单](https://segmentfault.com/a/1190000018756503)
- 作者:[前端小智](https://segmentfault.com/a/1190000017794020)
**[Fundebug](https://www.fundebug.com/)经授权转载,版权归原作者所有。**
Web 开发最常用的高度就是 `console.log` ,虽然 `console.log` 占有一席之地,但很多人并没有意识到 `console` 本身除了基本 `log` 方法之外还有很多其他方法。 适当使用这些方法可以使调试更容易,更快速,更直观。
### console.log()
在`console.log` 中有很多人们意想不到的功能。虽然大多数人使用 `console.log(object)` 来查看对象,但是你也可以使用 `console.log(object, otherObject, string)`,它会把它们都整齐地记录下来,偶尔也会很方便。
不仅如此,还有另一种格式化的: `console.log(msg, values)`,这很像 C 或 PHP 中的`sprintf`。
```javascript
console.log("I like %s but I do not like %s.", "Skittles", "pus");
```
会像你预期的那样输出:
```javascript
> I like Skittles but I do not like pus.
```
常见的占位符 `%o` (这是字母 o,不是 0),它接受对象,`%s` 接受字符串,`%d` 表示小数或整数。

另一个有趣的是 `%c`,这可能与你所想不太相同,它实际上是 CSS 值的占位符。使用%c 占位符时,对应的后面的参数必须是 CSS 语句,用来对输出内容进行 CSS 渲染。常见的输出方式有两种:`文字样式、图片输出`。
```javascript
console.log(
"I am a %cbutton",
"color: white; background-color: orange; padding: 2px 5px; border-radius: 2px"
);
```

它并不优雅,也不是特别有用。当然,这并不是一个真正的按钮。

它有用吗? 恩恩恩。
### console.dir()
在大多数情况下,`console.dir()` 的函数非常类似于 `log()`,尽管它看起来略有不同。

下拉小箭头将显示与上面相同的对象详细信息,这也可以从`console.log` 版本中看到。当你查看元素的结构时候,你会发现它们之间的差异更大,也更有趣。
```javascript
let element = document.getElementById("2x-container");
```
使用 `console.log` 查看:

打开了一些元素,这清楚地显示了 DOM,我们可以在其中导航。但是`console.dir(element)`给出了更加方便查看 DOM 结构的输出:
这是一种更客观地看待元素的方式。有时候,这可能是您真正想要的,更像是检查元素。

**代码部署后可能存在的 BUG 没法实时知道,事后为了解决这些 BUG,花了大量的时间进行 log 调试,这边顺便给大家推荐一个好用的 BUG 监控工具 [Fundebug](https://www.fundebug.com/)。**
### console.warn()
可能是最明显的直接替换 `log()`,你可以以完全相同的方式使用 `console.warn()`。 唯一真正的区别是输出字的颜色是黄色的。 具体来说,输出处于警告级别而不是信息级别,因此浏览器将稍微区别对待它。 这具有使其在杂乱输出中更明显的效果。

不过,还有一个更大的优势,因为输出是警告而不是信息,所以你可以过滤掉所有`console.log`并仅保留`console.warn`。 这对于偶尔会在浏览器中输出大量无用废话的应用程序尤其有用。 清除一些无用的信息可以让你更轻松地看到你想要的输出。
### console.table()
令人惊讶的是,这并不是更为人所知,但是 `console.table()` 函数旨在以一种比仅仅转出原始对象数组更整洁的方式显示表格数据。
例如,这里有一个数据列表。
```javascript
const data = [
{
id: "7cb1-e041b126-f3b8",
seller: "WAL0412",
buyer: "WAL3023",
price: 203450,
time: 1539688433
},
{
id: "1d4c-31f8f14b-1571",
seller: "WAL0452",
buyer: "WAL3023",
price: 348299,
time: 1539688433
},
{
id: "b12c-b3adf58f-809f",
seller: "WAL0012",
buyer: "WAL2025",
price: 59240,
time: 1539688433
}
];
```
如果我们使用 `console.log` 来输出上面的内容,我们会得到一些非常无用的输出:
```javascript
▶ (3) [{…}, {…}, {…}]
```
点击这个小箭头可以展开看到对象的内容,但是,它并不是我们想要的“一目了然”。
但是 `console.table(data)` 的输出要有用得多。

第二个可选参数是所需列的列表。显然,所有列都是默认值,但我们也可以这样做:
```javascript
> console.table(data, ["id", "price"]);
```

这里要注意的是这是乱序的 - 最右边的列标题上的箭头显示了原因。 我点击该列进行排序。 找到列的最大或最小,或者只是对数据进行不同的查看非常方便。 顺便说一句,该功能与仅显示一些列无关,它总是可用的。
`console.table()` 只能处理最多 1000 行,因此它可能不适合所有数据集。
### console.assert()
`assert()` 与 `log()` 是相同的函数,`assert()`是对输入的表达式进行断言,只有表达式为 false 时,才输出相应的信息到控制台,示例如下:
```javascript
var arr = [1, 2, 3];
console.assert(arr.length === 4);
```

有时我们需要更复杂的条件句。例如,我们已经看到了用户 `WAL0412` 的数据问题,并希望仅显示来自这些数据的事务,这是直观的解决方案。
```javascript
console.assert(tx.buyer === "WAL0412", tx);
```
这看起来不错,但行不通。记住,条件必须为`false`,断言才会执行,更改如下:
```javascript
console.assert(tx.buyer !== "WAL0412", tx);
```
与其中一些类似,`console.assert()` 并不总是特别有用。但在特定的情况下,它可能是一个优雅的解决方案。
### console.count()
另一个具有特殊用途的计数器,count 只是作为一个计数器,或者作为一个命名计数器,可以统计代码被执行的次数。
```javascript
for (let i = 0; i < 10000; i++) {
if (i % 2) {
console.count("odds");
}
if (!(i % 5)) {
console.count("multiplesOfFive");
}
if (isPrime(i)) {
console.count("prime");
}
}
```
这不是有用的代码,而且有点抽象。这边也不打算演示 `isPrime` 函数,假设它是成立的。
执行后我们会得到一个列表:
```javascript
odds: 1
odds: 2
prime: 1
odds: 3
multiplesOfFive: 1
prime: 2
odds: 4
prime: 3
odds: 5
multiplesOfFive: 2
...
```
还有一个相关的 `console.countReset()`,可以使用它重置计数器。
### console.trace()
`trace()` 在简单的数据中很难演示。当您试图在类或库中找出是哪个实际调用者导致了这个问题时,它的优势就显现出来了。
例如,可能有 12 个不同的组件调用一个服务,但是其中一个组件没有正确地设置依赖项。
```javacript
export default class CupcakeService {
constructor(dataLib) {
this.dataLib = dataLib;
if(typeof dataLib !== 'object') {
console.log(dataLib);
console.trace();
}
}
...
}
```
这里使用 `console.log()` 仅告诉我们传递数据`dataLib`是什么 ,而没有具体的传递的路径。不过,`console.trace()` 会非常清楚地告诉我们问题出在 `Dashboard.js`,我们可以看到是 `new CupcakeService(false)` 导致错误。
### console.time()
`console.time()` 是一个用于跟踪操作时间的专用函数,它是跟踪 JavaScript 执行时间的好方法。
```javascript
function slowFunction(number) {
var functionTimerStart = new Date().getTime();
// something slow or complex with the numbers.
// Factorials, or whatever.
var functionTime = new Date().getTime() - functionTimerStart;
console.log(`Function time: ${functionTime}`);
}
var start = new Date().getTime();
for (i = 0; i < 100000; ++i) {
slowFunction(i);
}
var time = new Date().getTime() - start;
console.log(`Execution time: ${time}`);
```
这是一种老派的做法,我们使用 `console.time()` 来简化以上代码。
```javascript
const slowFunction = number => {
console.time("slowFunction");
// something slow or complex with the numbers.
// Factorials, or whatever.
console.timeEnd("slowFunction");
};
console.time();
for (i = 0; i < 100000; ++i) {
slowFunction(i);
}
console.timeEnd();
```
我们现在不再需要做任何计算或设置临时变量。
### console.group()
```javascript
// this is the global scope
let number = 1;
console.group("OutsideLoop");
console.log(number);
console.group("Loop");
for (let i = 0; i < 5; i++) {
number = i + number;
console.log(number);
}
console.groupEnd();
console.log(number);
console.groupEnd();
console.log("All done now");
```
输出如下:

并不是很有用,但是您可以看到其中一些是如何组合的。
```javascript
class MyClass {
constructor(dataAccess) {
console.group("Constructor");
console.log("Constructor executed");
console.assert(
typeof dataAccess === "object",
"Potentially incorrect dataAccess object"
);
this.initializeEvents();
console.groupEnd();
}
initializeEvents() {
console.group("events");
console.log("Initialising events");
console.groupEnd();
}
}
let myClass = new MyClass(false);
```

这是很多工作和很多调试信息的代码,可能不是那么有用。 但它仍然是一个有趣的想法,这样写使你的日志记录更加清晰。
### 选择 DOM 元素
如果熟悉 jQuery,就会知道 `$(‘.class’)` 和 `$(‘#id’)` 选择器有多么重要。它们根据与之关联的类或 ID 选择 DOM 元素。
但是当你没有引用 jQuery 时,你仍然可以在谷歌开发控制台中进行同样的操作。
$(‘tagName’) $(‘.class’) $You can't use 'macro parameter character #' in math mode(‘#id’) and $(‘.class #id’) 等效于`document.querySelector(‘ ‘)`,这将返回 DOM 中与选择器匹配的第一个元素。
可以使用 `\$\$(tagName)` 或 `\$\$(.class)`, **注意双元符号**,根据特定的选择器选择 DOM 的所有元素。这也将它们放入数组中,你也可以通过指定数组中该元素的位置来从中选择特定的元素。
例如,**\$\$(‘.className’)** 获取具有类 `className` 的所有元素,而`\$\$(‘.className’)[0]`和 `\$\$(‘.className’)[1]`获取到分别是第一个和第二个元素。

### 将浏览器转换为编辑器
你有多少次想知道你是否可以在浏览器中编辑一些文本? 答案是肯定的,你可以将浏览器转换为文本编辑器。 你可以在 DOM 中的任何位置添加文本和从中删除文本。
你不再需要检查元素并编辑 HTML。相反,进入开发人员控制台并输入以下内容:
```javascript
document.body.contentEditable = true;
```
这将使内容可编辑。现在,你几乎可以编辑 DOM 中的任何内容。
### 查找与 DOM 中的元素关联的事件
调试时,需要查找 DOM 中某个元素的事件侦听器感时,谷歌控制台了 `getEventListeners`使找到这些事件更加容易且直观。
`getEventListeners($(‘selector’))` 返回一个对象数组,其中包含绑定到该元素的所有事件。你可以展开对象来查看事件:

要找到特定事件的侦听器,可以这样做:
```javascript
getEventListeners($(‘selector’)).eventName[0].listener
```
这将显示与特定事件关联的侦听器。这里 `eventName[0]` 是一个数组,它列出了特定事件的所有事件。例如:
```javascript
getEventListeners($(‘firstName’)).click[0].listener
```
将显示与 ID 为 `‘firstName’` 的元素的单击事件关联的侦听器。
### 监控事件
如果希望在执行绑定到 DOM 中特定元素的事件时监视它们,也可以在控制台中这样做。你可以使用不同的命令来监控其中的一些或所有事件:
如果希望在执行绑定到 DOM 中特定元素的事件时监视它们,也可以在控制台中这样做。你可以使用不同的命令来监控其中的一些或所有事件:
- `monitorEvents($(‘selector’))` 将监视与选择器的元素关联的所有事件,然后在它们被触发时将它们打印到控制台。例如,`monitore($(#firstName))` 将打印 **ID** 为 `firstName`元素的所有事件。
- `monitorEvents($(‘selector’),’eventName’)` 将打印与元素绑定的特定事件。 你可以将事件名称作为参数传递给函数。 这将仅记录绑定到特定元素的特定事件。 例如,monitorEvents(\$(‘#firstName’),’click’) 将打印绑定到 ID 为'firstName'的元素的所有 `click` 事件。
- `monitore($(selector),[eventName1, eventName3', .])`将根据您自己的需求记录多个事件。与其传递单个事件名作为参数,不如传递包含所有事件的字符串数组。例如`monitore($(#firstName),[click, focus])`将记录与 ID firstName 元素绑定的 `click`事件和`focus`事件。
- `unmonitorevent ($(selector))`:这将停止监视和打印控制台中的事件。
### 检查 DOM 中的一个元素
你可以直接从控制台检查一个元素:
- inspect(\$将检查与选择器匹配的元素,并转到中的选项卡。例如,(‘selector’)) 将检查与选择器匹配的元素,并转到 Chrome Developer Tools 中的 \*\*Elements\*\* 选项卡。 例如, `inspect($(‘#firstName’))` 将检查 ID 为'firstName' 的元素,`spect($(‘a’)[3])` 将检查 DOM 中的第 4 个 `a` 元素。
- $0, $1, \$等可以帮助你获取最近检查过的元素。例如,2 等可以帮助你获取最近检查过的元素。 例如,`$0` 表示最后检查的 DOM 元素,而`$1` 倒数第二个检查的 DOM 元素。
### 检索最后一个结果的值
你可以将控制台用作计算器。当你这样做的时候,你可能需要用第二个来跟踪一个计算。以下是如何从内存中检索先前计算的结果:
```javascript
$_;
```
过程如下:
```javascript
2 + 3 + 4;
9; //- The Answer of the SUM is 9
$_;
9; // Gives the last Result
$_ * $_;
81; // As the last Result was 9
Math.sqrt($_);
9; // As the last Result was 81
$_;
9; // As the Last Result is 9
```
### 清除控制台和内存
如果你想清除控制台及其内存,输入如下:
```javascript
clear();
```
**原文:**
- [Beyond console.log()](https://medium.com/@mattburgess/beyond-console-log-2400fdf4a9d8)
- [Things you probably didn’t know you could do with Chrome’s Developer Console](https://medium.freecodecamp.org/10-tips-to-maximize-your-javascript-debugging-experience-b69a75859329)
### 关于Fundebug
[Fundebug](https://www.fundebug.com/)专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家[免费试用](https://www.fundebug.com/team/create)!

- 如何高效地遍历超大MongoDB集合
- 深入理解 JavaScript 执行上下文和执行栈
- JavaScript 为什么要有 Symbol 类型?
- Web 性能优化:21 种优化 CSS 和加快网站速度的方法
- 深入理解 JavaScript 作用域和作用域链
- WEB 实时推送技术的总结
- 前端面试:谈谈 JS 垃圾回收机制
- TypeScript,初次见面,请多指教 ?
- 使用这些 HTTP 头保护 Web 应用
- JavaScript 的 4 种数组遍历方法: for VS forEach() VS for/in VS for/of
- 装上这几个 VSCode 插件后,上班划水摸鱼不是梦
- 一文搞懂TCP与UDP的区别
- 如何优雅地查看 JS 错误堆栈?
- 一文读懂 HTTP/2 及 HTTP/3 特性
- Web 性能优化: 图片优化让网站大小减少 62%
- 我们应该如何给需求排序?
- Web 性能优化: 使用 Webpack 分离数据的正确方法
- 2019 前端面试题汇总(主要为 Vue)
- 高效使用VSCode的9点建议
- 小程序多端框架全面测评:chameleon、Taro、uni-app、mpvue、WePY
- 灵活使用 console 让 js 调试更简单
- 深入了解浏览器存储:对比Cookie、LocalStorage、sessionStorage与IndexedDB
- JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!
- Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级
- 如何来一次说干就干的重构 (流程篇)
- JavaScript 新语法详解:Class 的私有属性与私有方法
- JavaScript 原型的深入指南
- 一个程序员的自我修养
- 为什么HTTPS比HTTP更安全?