控制台应该是大多数前端开发人员日常开发调试离不开的神器。然而控制台仍有很多不为人知的属性及方法,能让你更爽地使用,当然也包括了一些隐藏的深坑(console.log
对象打印bug #feature)...
Try it first!
开始前,让我们来造一个马里奥!
打开开发者工具的控制台,将下述代码复制粘贴,然后猛敲回车!!(navigator.userAgent.toLowerCase().indexOf('chrome') > -1) ? null : (function() { var args = [], eightBitHack = [], coordinates = ["41n8r2", "42t3wu", "449u8a", "4h4014", "4h2c4y", "4g6ia1", "4286dm", "447r6w", "4fudcv", "61z2xp", "70rmyd", "71sfq1", "6zgplp", "42spfv", "4frvnp", "61wzpd"]; for (var row = coordinates.length; row--;) { var decompressedRow = parseInt(coordinates[row], 36).toString(5).split(''); coordinates[row] = decompressedRow.splice(1, decompressedRow.length-1); for (var cell = coordinates[row].length; cell--;) { var dot = parseInt(coordinates[row][cell]); var color = dot === 4 ? '#ecd585' : dot === 3 ? '#e1c25b' : dot === 2 ? '#805936' : dot ? '#ec2733' : '#fff'; args.unshift("border: 8px solid color;".replace('color', color)); eightBitHack.unshift("%c"); } eightBitHack.unshift("\n"); } eightBitHack.push("%c\n\n\n", "\nIt's me, Mario!", "\nmade by %chttps://twitter.com/aristretto"); args.push("font-weight: bold;", "font-weight: bold; color: teal;"); args.unshift(eightBitHack.join('')); console.log.apply(console, args);})();复制代码
可以看到,一个充满色彩的马里奥出现在了控制台
主要是使用了以下命令实现: console.log('%c','/*css*/');
*当然这里面还有到了其他的trick处理方式,详情请看
控制台进阶玩法
从上述的例子可以看出,控制台还有很多我们不知道的进阶玩法,如输出更好的格式,利用一些trick,使得我们的调试更具效率。
Console API
在控制台中输入console.
可以看出,console中有很多方法可以调用,网上已有很多资源说明,此次仅提及几个比较有用的API
console.log(object[,object,...])
console.log
除了常见的将需要输出的结果直接传入第一个参数中,还有以下用法:
- 使用逗号分隔,传入多个参数输出 输出时会将每个逗号自动替换成空格
console.log('Hello','world!\t','Current Time:',Date.now());// Hello world! Current Time: 1530694211342复制代码
- 使用说明符输出,类似于C++中的
print
函数 除了一般的%s
(字符串)、%i``%d
(均为整型)、%f
外,还支持%c
(样式)、%o
(DOM元素)、%O
(JavaScript对象)输出
console.log('Hello world!\t%s: %i','Current Time',Date.now());console.log("normal text,%c large blue text,%c white text with black background ", "color: blue; font-size: x-large","color:white;background:black;");console.log('%o\n%O',document,{ a:1,b:2,c:3});复制代码
上述代码输出结果见下图:
console.count
写入在同一行使用相同标签调用count()
的次数,可用于某些setInterval
或者事件重复触发的调试。
const fn = function(name){ console.count(name);};fn('Bob'); // Bob: 1fn('John'); // John: 1fn('Bob'); // Bob: 2fn('Bob'); // Bob: 3复制代码
console.error console.trace
输出一条消息,并包含了调用该方法的地方的堆栈信息。区别是error
会将消息设置成错误的样式。
(()=>{ const fn1 = (fn)=>{ fn(); }; const fn2 = ()=>{ console.trace('Target Not Found'); console.error('Target Not Found'); }; fn1(fn2);})();复制代码
上述代码输出结果见下图:
console.time timeEnd
启动一个具有关联标签的新计时器。使用相同标签调用console.timeEnd()
时,定时器将停止,经过的时间将显示在控制台中。计时器值精确到亚毫秒。传递到time()
和timeEnd()
的字符串必须匹配,否则计时器不会结束。
console.time('test');for(let i = 0; i < 100000000; i++){}console.timeEnd('test');// 输出:// test: 122.71923828125ms复制代码
Others
此外,还有很多好用的Console API,如console.table
、console.group
、console.assert
等。可以在Chrome的文档中找到他们的使用方法.
Command Line API
尝试一下,在一个未引入jQuery
和zepto
的页面的控制台中,直接输入$
、$$
会出现什么?
// 直接打开控制台输入console.log($,$$);// 输出:// ƒ $(selector, [startNode]) { [Command Line API] } ƒ $$(selector, [startNode]) { [Command Line API] }复制代码
可以看到,输出的函数中,包含了[Command Line API]
。Command Line API
是由控制台提供的一系列便捷函数集合,大概的功能有:选择和检查 DOM元素,以可读格式显示数据,停止和启动分析器,以及监控 DOM 事件。
$、$$
$(selector)
等同于document.querySelector
,同样的,$(selector)
等同于document.querySelectorAll
。Command Line API
只是提供了较快捷的方式便于开发者进行调试。
$0-$4
$0
、$1
、$2
、$3
和$4
命令用作在 Elements 面板中检查的最后五个DOM元素或在 Profiles面板中选择的最后五个JavaScript堆对象的历史参考。$0
返回最近一次选择的元素或JavaScript对象,$1
返回仅在最近一次之前选择的元素或对象,依此类推。
others
此外,还有很多好用的Command Line API,如copy
、debug
、monitor
、profile
等。可以在Chrome的文档中找到他们的使用方法.
控制台的坑
试想一下以下代码在控制台中输出的结果:
const fn = function(length){ const o = { arr: [], key1: 'test', key2: 'test', key3: 'test', key4: 'test', key5: 'test', index: 0 }; console.log(JSON.stringify(o)); console.log(o); console.log('Handling data'); for(let i = 0; i < length; i++){ o.arr.push(i); } o.index = length; console.log(JSON.stringify(o)); console.log(o);};fn(5);复制代码
不难看出,控制台中输出的结果应该如下图:
此时,我们展开一下第二行与第五行,会发现一个很奇怪的现象:
展开第二行发现,arr里的长度是5,对象的index值居然是5!延时计算问题
定位上述的问题,只需要将鼠标移至行尾的蓝色info标记上,控制台会提示以下内容:
Value below was evaluated just now. #所以这不是bug是feature
这句话意味着,展开当前的object的时候,控制台才会去计算出这个对象的key-value,再反馈到控制台中显示。 使用Console打印的时候,若控制台发现当前需要打印的内容是一个对象,会将其保存下来,先输出一个简要的快照(Snapshot),待开发者需要查看其中详细内容时,再点击展开,返回内存中的值。 这个是控制台的一个已知的坑点,有可能设计该功能是为了避免控制台对大对象深复制输出,导致调试过慢,也有可能是为了方便查看原型链上的属性,但这无疑是开发者调试代码时需要避开的问题。 要避免这种调试问题,建议使用JSON.stringify()进行输出调试,而不是直接打印当前对象。 内存泄漏问题
上面提到,使用Console打印对象时,会将这个对象的引用保存下来。由于开发者工具在浏览器中默认开启,且默认了“开发者之后需要查看该对象”的行为,就会导致在Console中引入的对象是不会进入GC(垃圾回收)逻辑中的,这就引发了内存泄漏问题。 要避免内存泄漏问题,需要将开发环境与线上环境进行分离,线上环境中避免产生控制台打印的语句,亦可以在项目打包时,将ESLint
中的no-console
的开关打开。