**免责声明:**最近一直在研究微前端和devops。写这篇文章只是一个玩笑+简单的源码探索。面试的时候不要问面试官我的文章,不然怕被拒人身攻击(本月发硬核到头皮发麻的文章)废话不多说,直接开始,找控制台module,找到导入的模块,进入比较简单,默认暴露globalConsole。写在一篇烂文里(之前感觉很烂)源码精读:通过Node.jsCluster模块源码,深入PM2原理原创精读:来自Node.jsjs的源码是commonJS的模块化方案,很多都是挂载在prototype上提供调用,但是在目前的开发中,并没有给prototype添加属性。看到了Reflect.defineProperty这些熟悉的vue2.x源码,还有ES6的Reflect.ownKeys获取所有属性集合Reflect.getOwnPropertyDescriptor获取属性描述符。不明白的可以看https://es6.ruanyifeng.com/#docs/反映这个入口代码:constglobalConsole=Object.create({});for(constpropofReflect.ownKeys(Console.prototype)){if(prop==='constructor'){继续;}constdesc=Reflect.getOwnPropertyDescriptor(Console.prototype,prop);if(typeofdesc.value==='function'){//修复接收器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.与源码类似vue2.x的实现,使用如下API,指定property读取劫持,比如我使用console.log时,会触发Reflect.defineProperty(globalConsole,prop,desc)5.真正的原理在后面,看看在原型上安装的构造函数的Console上导入的Console是什么熟悉的味道。先看核心代码:for(constmethodofReflect.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={控制台,kBindStreamsLazy,k绑定属性};发现consoleMethods就是我们要遍历一次的,将consoleMethods的所有方法复制到Console的原型中,这样我们就可以调用console.log了,那么log方法怎么实现呢?日志(...args){this[kWriteToConsole](kUseStdout,this[kFormatForStdout](args));},最后依赖this.kWriteToConsole,也就是Console的实现(kWriteToConsole是Symbol的一个临时属性)。这里关键是kUseStdout也是一个Symbol临时属性,kFormatForStdout有点混淆,我们看一下kFormatForStdoutConsole.prototype[kFormatForStdout]=function(args){constopts=this[kGetInspectOptions](this._stdout);returnformatWithOptions(opts,...args);};这里是对颜色做一个处理,没有过多的处理,都在这个模块中,声明的地图类型存储Console.prototype[kGetInspectOptions]=function(stream){letcolor=this[kColorMode];if(color==='auto'){color=stream.isTTY&&(typeofstream.getColorDepth==='function'?stream.getColorDepth()}>2:真);}constoptions=optionsMap.get(this);if(options){if(options.colors===undefined){options.colors=color;}返回选项;}返回颜色?kColorInspectOptions:kNoColorInspectOptions;};处理打印颜色配置后,进入最终函数:Console.prototype[kWriteToConsole]=function(streamSymbol,string){constignoreErrors=this._ignoreErrors;constgroupIndent=this[kGroupIndent];constuseStdout=streamSymbol===kUseStdout;conststream=useStdout?this._stdout:this._stderr;consterrorHandler=useStdout?this._stdoutErrorHandler:this._stderrErrorHandler;这里需要重点关注stream的取值,这个模块在这个模块中多次出现,我们再看看其他地方(与本文源码无关)conststream=streamSymbol===kUseStdout?instance._stdout:instance._stderr;官方说明://当且仅当此条件评估为真therewasanerror表示当发生错误时,打印errorif(ignoreErrors===false)returnstream.write(string);try{//添加并稍后删除一个noop错误处理程序以捕获同步错误。if(stream.listenerCount('错误')===0)stream.once('错误',noop);stream.write(string,errorHandler);最后使用stream.write(string)打印。觉得写的不错记得点赞关注我哦。
