前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >无情面试官:Node.js源码里的console.log怎么实现的?

无情面试官:Node.js源码里的console.log怎么实现的?

作者头像
Peter谭金杰
发布2020-05-09 17:35:29
2K0
发布2020-05-09 17:35:29
举报
文章被收录于专栏:跨平台全栈俱乐部

**声明: **

最近一直在研究微前端、devops,写这篇文章仅是一个玩笑+简单的源码探究,面试时候不要拿我的文章出来问面试者,不然我怕你会被人身攻击(这个月我会出一篇硬核到头皮发麻的文章)


废话不多,直接开始,找到console的模块,找到引入的模块,进入

还是比较简单的,默认暴露globalConsole

我之前在这两个烂文章里写过(之前写的感觉就是很烂)

源码精读:通过Node.js的Cluster模块源码,深入PM2原理

原创精读:从Node.js的path模块源码,彻底搞懂webpack的路径

Node.js的源码是commonJS模块化方案,很多都是挂载到原型上提供调用,但是在现在的开发中,千万不要在原型上添加属性。

看到了Reflect.defineProperty

这些似曾相识的vue 2.x源码


里面还有ES6的Reflect.ownKeys获得所有属性集合

Reflect.getOwnPropertyDescriptor得到属性描述符

还不了解的可以看

代码语言:javascript
复制
https://es6.ruanyifeng.com/#docs/reflect

这段入口的代码:

代码语言:javascript
复制
const globalConsole = Object.create({});
for (const prop of Reflect.ownKeys(Console.prototype)) {
if (prop === 'constructor') { continue; }
const desc = Reflect.getOwnPropertyDescriptor(Console.prototype, prop);
if (typeof desc.value === 'function') { // fix the receiver
    desc.value = desc.value.bind(globalConsole);
  }
Reflect.defineProperty(globalConsole, prop, desc);
}
globalConsole[kBindStreamsLazy](process);
globalConsole[kBindProperties](true, 'auto');
globalConsole.Console = Console;
module.exports = globalConsole;

核心逻辑:

1.先生成一个纯净的对象

2.遍历原型上的属性 如果是构造函数就跳过

3.获取它的访问描述符,重新生成挂载到desc(访问描述符上)

4.类似vue 2.x的源码实现,使用下面的API,指定属性读取劫持,例如我使用console.log时候,就会触发 Reflect.defineProperty(globalConsole, prop, desc)

5.真正的原理在后面,constructor的Console上


看看引入的Console是什么

熟悉的味道,挂载到的是原型上。

先看核心代码:

代码语言:javascript
复制
for (const method of Reflect.ownKeys(consoleMethods))
  Console.prototype[method] = consoleMethods[method];

Console.prototype.debug = Console.prototype.log;
Console.prototype.info = Console.prototype.log;
Console.prototype.dirxml = Console.prototype.log;
Console.prototype.error = Console.prototype.warn;
Console.prototype.groupCollapsed = Console.prototype.group;

module.exports = {
  Console,
  kBindStreamsLazy,
  kBindProperties
};

发现consoleMethods就是我们想要的

遍历了一次,将consoleMethods的方法都拷贝到了Console的原型上,这样我们就可以调用console.log了

那么log方法怎么实现的呢?

代码语言:javascript
复制
log(...args){
this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args));
  },

最终是靠this.kWriteToConsole,也就是Console实现(kWriteToConsole是一个Symbol临时属性)

关键这里kUseStdout也是一个Symbol临时属性,kFormatForStdout有一丢丢绕,我们看看kFormatForStdout

代码语言:javascript
复制
Console.prototype[kFormatForStdout] = function(args) {
  const opts = this[kGetInspectOptions](this._stdout);
  return formatWithOptions(opts, ...args);
};

这里是对颜色做一个处理,不做过度处理,都在本模块内,声明的map类型内存储

代码语言:javascript
复制
Console.prototype[kGetInspectOptions] = function(stream) {
let color = this[kColorMode];
if (color === 'auto') {
    color = stream.isTTY && (
typeof stream.getColorDepth === 'function' ?
        stream.getColorDepth() > 2 : true);
  }

const options = optionsMap.get(this);
if (options) {
if (options.colors === undefined) {
      options.colors = color;
    }
return options;
  }
return color ? kColorInspectOptions : kNoColorInspectOptions;
};

处理完打印颜色配置,进入最终函数:

代码语言:javascript
复制
Console.prototype[kWriteToConsole] = function(streamSymbol, string) {
const ignoreErrors = this._ignoreErrors;
const groupIndent = this[kGroupIndent];
const useStdout = streamSymbol === kUseStdout;
const stream = useStdout ? this._stdout : this._stderr;
const errorHandler = useStdout ?
this._stdoutErrorHandler : this._stderrErrorHandler;

这里我们需要重点观察下stream这个值,在这个模块出现过很多次,我们看看其他地方(跟本文的源码无关)

代码语言:javascript
复制
const stream = streamSymbol === kUseStdout ?
 instance._stdout : instance._stderr;

官方注释:

代码语言:javascript
复制
// This conditional evaluates to true if and only if there was an error

意思是出现错误时候,打印error。

代码语言:javascript
复制
if (ignoreErrors === false) return stream.write(string);
try {
// Add and later remove a noop error handler to catch synchronous errors.
if (stream.listenerCount('error') === 0)
      stream.once('error', noop);
    stream.write(string, errorHandler);

最终使用stream.write(string)打印完成。

觉得写得不错,记得点个赞,关注下我。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档