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

9f18bdddabd46fda986364463ca51406.png

声明:

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


ee228ae3e19c6fcd833fe38f17b60ec6.png

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

0b0e9316ed4b484b5c8aa275ca1e3133.png

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

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

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

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

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

看到了Reflect.defineProperty  

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


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

16cf51c2e4610fc73869b6988065d3c1.png

Reflect.getOwnPropertyDescriptor得到属性描述符

f8fd82f12125d784a0a81547cb580b56.png

还不了解的可以看

https://es6.ruanyifeng.com/#docs/reflect

这段入口的代码:

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上

0be11537862c9050d698d02739103d7a.png


看看引入的Console是什么

d556bc3a8a3bd3c798537186fd2603f1.png

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

先看核心代码:

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就是我们想要的

fa50c1ec30d671cc390a59f4eb2063d8.png

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

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

log(...args) {this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args));  },

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

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

Console.prototype[kFormatForStdout] = function(args) {  const opts = this[kGetInspectOptions](this._stdout);  return formatWithOptions(opts, ...args);};

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

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;};

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

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这个值,在这个模块出现过很多次,我们看看其他地方(跟本文的源码无关)

  const stream = streamSymbol === kUseStdout ? instance._stdout : instance._stderr;

官方注释:

// This conditional evaluates to true if and only if there was an error// that was not already emitted (which happens when the _write callback// is invoked asynchronously).

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

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)打印完成。

最后

  • 欢迎加我微信(CALASFxiaotan),拉你进技术群,长期交流学习...

  • 欢迎关注「前端巅峰」,认真学前端,做个有专业的技术人...

69cec57466d5f721bf03a8737baa2c06.png 点个在看支持我吧,转发就更好了

好文我在看?

/usr/bin/ld: CMakeFiles/global_planning_node.dir/src/global_planning_node.cpp.o: in function main.cold': global_planning_node.cpp:(.text.unlikely+0x273): undefined reference to tf::TransformListener::~TransformListener()' /usr/bin/ld: CMakeFiles/global_planning_node.dir/src/global_planning_node.cpp.o: in function main': global_planning_node.cpp:(.text.startup+0xc64): undefined reference to tf::Transformer::DEFAULT_CACHE_TIME' /usr/bin/ld: global_planning_node.cpp:(.text.startup+0xc92): undefined reference to tf::TransformListener::TransformListener(ros::Duration, bool)' /usr/bin/ld: global_planning_node.cpp:(.text.startup+0xd7a): undefined reference to tf::Transformer::lookupTransform(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::_cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ros::Time const&, tf::StampedTransform&) const' /usr/bin/ld: global_planning_node.cpp:(.text.startup+0xe74): undefined reference to tf::TransformListener::~TransformListener()' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/global_planning_node.dir/build.make:246: /home/juan/catkin_ws/devel/.private/putn/lib/putn/global_planning_node] Error 1 make[1]: *** [CMakeFiles/Makefile2:207: CMakeFiles/global_planning_node.dir/all] Error 2 make[1]: *** Waiting for unfinished jobs.... /usr/bin/ld: CMakeFiles/local_obs_node.dir/src/local_obs.cpp.o: in function rcvVelodyneCallBack(sensor_msgs::PointCloud2<std::allocator<void> > const&)': local_obs.cpp:(.text+0xa0b): undefined reference to tf::Transformer::waitForTransform(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ros::Time const&, ros::Duration const&, ros::Duration const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const' /usr/bin/ld: local_obs.cpp:(.text+0xc74): undefined reference to tf::TransformListener::transformPoint(std::cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, geometry_msgs::PointStamped<std::allocator<void> > const&, geometry_msgs::PointStamped<std::allocator<void> >&) const' /usr/bin/ld: CMakeFiles/local_obs_node.dir/src/local_obs.cpp.o: in function main.cold': local_obs.cpp:(.text.unlikely+0x37d): undefined reference to tf::TransformListener::~TransformListener()' /usr/bin/ld: CMakeFiles/local_obs_node.dir/src/local_obs.cpp.o: in function main':local_obs.cpp:(.text.startup+0x62a): undefined reference to tf::Transformer::DEFAULT_CACHE_TIME' /usr/bin/ld: local_obs.cpp:(.text.startup+0x64d): undefined reference to tf::TransformListener::TransformListener(ros::Duration, bool)' /usr/bin/ld: local_obs.cpp:(.text.startup+0x6dc): undefined reference to tf::TransformListener::~TransformListener()' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/local_obs_node.dir/build.make:246: /home/juan/catkin_ws/devel/.private/putn/lib/putn/local_obs_node] Error 1 make[1]: *** [CMakeFiles/Makefile2:612: CMakeFiles/local_obs_node.dir/all] Error 2 make: *** [Makefile:141: all] Error 2解释编译时出现这个问题的原因,并说说如何解决
07-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值