在构思和编写本文的时候,笔者脑海里经常涌现出上学时孔乙己展现“回”字的四种写法的场景。但现在觉得能够更轻松客观的来看待这些问题。就是有时魔鬼确实就在细节当中,有些看起来意义不大,但其实只是我们的理解和认知的层次不够深入和丰富。
关于console
console,就是控制台,这是很多开发工具和语言为了开发方便,提供给开发人员的一个工具。开发者可以使用它在程序执行过程中,在默认输出机制中,定义并输出一些程序内容,如变量值,执行进度,状态等等,方便开发者调试程序或者监控程序执行状态。通常也被称为是日志记录和日志系统。
console是nodejs提供的工具名称,这个工具在浏览器的JS环境和调试工具中都可以使用。在其他的开发工具或者平台中,一般也有类似的东西,也可能不叫console,如JAVA通常使用System.println。但无论叫什么名字,其机制和目的都是类似的。
console的输出一般都是所在执行环境或者操作系统的输出机制,如nodejs本身需要从命令行启动,其默认输出就是其启动命令使用的终端或者命令界面。而在浏览器中,输出就是浏览器的调试工具所提供的控制台(图)。
console对象和重定向
在nodejs中,console是一个全局对象,可以直接使用(不需要require引用,也应该默认是一个单例)。其实,Console也是一个类,开发者也可以定义Console的实例,来自定义日志记录和采集的过程。默认控制台是输出到系统标准输出,但如果想要输出到日志文件,可以使用如下的方式:
const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// Custom simple logger
const logger = new Console({ stdout: output, stderr: errorOutput });
// use it like console
const count = 5;
logger.log('count: %d', count);
// In stdout.log: count 5
这段代码创建了一个console实例,但修改了其默认的输出和错误输出,到所定义的文件而部署标准控制台。后续的使用和标准console是一样的。
输出的类型
console的默认的类型是log,就是调用console.log()方法。但其实console还支持info、debug、warning、error等方法。它们可以对应系统日志的不同级别。方便后续的过滤和处理。但在正常的开发过程中,它们的区别很少,被应用的场景也比较少。
console.error()稍微有点特殊,默认情况下,它的输出通道是stderr。
输出内容和形式
除了可以直接打印变量的值之外,console其实还可以使用其他方式来输入内容
- 输出参数
console方法一般都支持多个参数。它会依次打印这些参数的值,以空格作为分隔。
- 计数器: count()
console.count(label),可以实现一个计数器,每次程序运行到这里,都会+1,特别有助于在循环调用过程中,检查程序的运行状况。
- 输出表格: table(array)
如果想要以表格的形式,打印一个数组,可以使用如下的方式:
// 默认表格
console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
┌─────────┬─────┬─────┬──────┐
│ (index) │ a │ b │ d │
├─────────┼─────┼─────┼──────┤
│ 0 │ 1 │ 'Y' │ 'D2' │
│ 1 │ 'Z' │ 2 │ │
└─────────┴─────┴─────┴──────┘
// 自定义列
> console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2, c: "CC" }], ['a','c']);
┌─────────┬─────┬──────┐
│ (index) │ a │ c │
├─────────┼─────┼──────┤
│ 0 │ 1 │ │
│ 1 │ 'Z' │ 'CC' │
└─────────┴─────┴──────┘
- 分组: group()
使用console.group()可以对输出信息进行分组,其实就是进行缩进排版,让输出结果更加易读。这个方法需要指定一个分组名称来标识分组。group()和groupEnd()可以配合使用,来清除和结束一个分组。
- 清理: clear()
如果标准输出是终端的话,使用console.clear()可以清理屏幕。有点像终端命令clear。
断言: asset()
使用断言,可以条件化的输出日志信息。
console.assert(true, 'does nothing');
console.assert(false, 'Whoops %s work', 'didn't');
// Assertion failed: Whoops didn't work
console.assert();
// Assertion failed
在使用assert时需要注意,第一个参数是一个判断条件,后面的参数是具体输出内容,但只有判断为false的时候,才会真正的输出。输入的内容格式是固定的,都为: "Assertion falied: ..."。
计时和性能评估
使用console.time可以用来给程序运行计时和进行性能的分析。如果没有这个方法,我们可能需要自己来定义计时开始和结束时间,并且计算耗时。它可以简化这方面的操作。它的使用方法很简单,先用console.time(label)定义一个计时器,参数为计时器标签名称;然后在需要计时的地方调用console.timeLog(label, data)来计时,还可以顺便输出一些变量的值;最后可选在适当的地方调用console.timeEnd(label),系统运行时就会打印结束时的耗时。示例代码如下:
// 使用标签定义计时器
console.time("SomeLabel");
... some code running
console.timeLog("SomeLabel", somevalue);
... other code
// 提供标签关闭计时器并打印耗时
console.timeEnd("SomeLabel");
// 显示耗时
Lable: 100ms
trace跟踪
这也是一个比较有趣的功能。在程序运行过程中合适的位置,调用trace(label)方法,可以输入当前代码在原代码中的文件和位置,和调用的堆栈。这个可以很好的帮助开发者了解和清理当前程序和模块间的调用关系。
...somecode
console.trace("Show");
Trace: Show
at doJob (C:WorkDevcolgupCtoolsstuphotoindex.js:30:12)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
检查器方法
如果开发者需要使用高级的调试工具,如在启动时使用了 --inspector 来启用nodejs inspector检查器的话,可以使用到一些只能在检查器环境中使用的console方法。如 profile、profileEnd、timeStamp等等,都是和V8引擎相关的。
笔者相信读者中应该没有多少人有能力和机会开发调试工具和性能监测工具,这里只是告诉大家nodejs是提供了这样一种可能的。
REPL
严格的说,这部分内容和console没有直接的关系。但笔者觉得这个东西和console的形式和逻辑很像,而且对于开发者而言,也是一个非常有用的工具,也值得分享一下。
在命令行方式下,nodejs带js文件名作为参数,可以执行这个JS文件。如果不带参数,直接执行Nodejs,将会进入REPL环境。这是内置在Nodejs环境中的一个交互式js代码执行环境。
REPL的意思是Read(读取)-Eval(执行)-Print(打印)-Loop)环境,可以让我们直接在命令行中进行 JavaScript 代码的交互执行。
这是一个简单的交互执行过程,开发者不需要打开开发工具,编写代码,启动执行和调试过程,就可以执行JS语句。Eval在这里是一个JS全局方法,它可以运行一段js代码(以其代码文本字符串作为输入参数)。
使用REPL,开发者可以快速的检查js程序和函数的相关语法,快速验证某些执行和算法,并熟悉一些内置对象和方法的使用方式。 下面是一些示例:
// console的用法
> console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
┌─────────┬─────┬─────┐
│ (index) │ a │ b │
├─────────┼─────┼─────┤
│ 0 │ 1 │ 'Y' │
│ 1 │ 'Z' │ 2 │
└─────────┴─────┴─────┘
undefined
// sha1编码
> crypto.createHash("SHA1").update("what's up").digest("hex");
'996ba4a8f1062612f66bac9777e2703bc9e49cc2'
// buffer的构造和转换
> Buffer.from("中文支持").toString("base64url")
'5Lit5paH5pSv5oyB'
// 二进制数表示
> (0b10001000).toString()
'136'
// 二进制数解析
> parseInt("10001000",2)
// 二进制数移位
> (0b10100111 >> 3).toString(2)
'10100'
// 位运算
> 197 & 4
4
> 197 | 4
197
> 197 ^ 4
193
// 数学计算
> 111**3
1367631
> 111 ** (1/3)
4.805895533705332
// 随机数和字符串生成
> 0|Math.random()*1000
986
> Math.random().toString(36).slice(2,10)
'6xskm5cz'
// 当前时间和转换
> 0|Date.now()/30000
56512064
> new Date().toISOString()
'2023-09-22T05:56:22.757Z'
输入.exit,或者Ctrl-C可以退出REPL环境。
小结
本文总结了nodejs中console的概念和一些用法,并介绍了REPL这个交互工具,希望能够帮助读者发现新知,助力工作。