在看一些优秀的源码的过程中,经常能学到一些新的东西。一些没用过的api、一些组织代码的小技巧、一种新的思路等。这也是阅读源码的乐趣。
这篇文章是把我学到的一些知识点做了一些整理。
目录如下:
requre.main区分入口模块
require.cache 操作缓存模块
require.resolve解析模块的绝对路径
块级作用域
位运算符实现取整逻辑
使用位运算符 + indexOf判断包含
暴露Promise的resolve实现控制权的转交
undefined的另一种写法
通过访问offsetWidth手动触发reflow
一种生成随机字符串的技巧
通过get移除constructor中的属性定义
requre.main区分入口模块
require.main
指向的是主模块,node的文档中是这么写的。
所以我们可以通过require.main === module
来判断是入口模块还是require引入的模块,可以做一些不同的逻辑处理。
这和python代码中常见的判断__name__
的代码是相同的作用。
require.cache 操作缓存模块
require.cache会在require之后,把exports的内容缓存在对象中,key为模块的绝对路径
当手动删除require.cache中的内容,再次require的时候会重新执行模块中的逻辑。
比如react-scripts中就用到了这个api,保证再次require时会再次执行模块中的逻辑。
require.resolve解析模块的绝对路径
这个api只是根据require的机制解析模块的绝对路径并返回,并不会require该模块。
require.cache的key就是模块的绝对路径,可以通过require.resolve(模块名)拿到。
有一个应用场景是这样的:我们开发了一个npm包,里面封装了webpack的配置,使用了一些loader
当别的包引入这个包并进行打包时会找不到这个loader。因为当require这个包时并不会处理loader的路径,而在运行时才会去查找,而项目中没有安装这些loader,就会报错。
解决这个问题就可以使用requre.resolve,在require这个包的时候就把路径变成绝对路径,这样运行时就能找到对应的loader了。
块级作用域
写node某一个模块的代码时,有时为了避免命名冲突,会使用自执行函数来包裹
这样变量不会污染模块作用域的变量。
但这里没必要生成一个函数,可以使用块级作用域的方式。
node的源码中可以看到大量使用了这种写法
位运算符实现取整逻辑
取整逻辑可以使用位运算符 按位或|
或者 按位取反~
来实现
写法很多,但建议统一一种,不要一种逻辑在同一个项目中多种写法。
使用位运算符 + indexOf判断包含
对-1取反的操作结果是0,而String.prototyep.indexOf结果为-1表示不包含,结合这两者可以写出下面的代码:
当然这里有些绕,不如直接用 != 1 或者includes等来判断直观。但看到这种写法要能理解。
暴露Promise的resolve实现控制权的转交
通过把promise的resolve方法暴露给调用者,可以实现代码内部流程交给调用者控制。
axios中CancelToken机制实现中有这样一段代码
参数为一个函数,内部创建了一个promise,然后给回调函数传递的参数中调用了这个promise。
这里executor的参数c就是上面调用resolve的那个函数。source方法把它暴露了出去,也就是把resolve的调用权交给了使用者。
使用者这样使用
通过调用cancal方法也就是间接调用了内部promise的resolve方法,使得流程能继续进行。
而接下来的内部流程是这样的
这样就实现了调用CancalToken的cancel方法,请求就会终止的功能。
这种把内部promise的resolve暴露出去实现流程控制权的转交的方式让人眼前一亮。最原始的方式可能是回调,创建CancelToken时传入一个回调函数,然后调用cancel时回调这个函数。这样也可以但是不如promise对象的方式优雅。
undefined的另一种写法
undefined
并不是一个关键字,是可以作为变量名定义的,这可能会导致安全问题。
所以建议使用 void 0
来代替undefined,当然void 加任何值都是undefined。
通过访问offsetWidth手动触发reflow
通过访问元素的offsetWidth,浏览器会计算这个值,能够触发reflow。
一种生成随机字符串的技巧
Math.random()是生成0到1的随机数,toString可以指定进制,这里的36进制包含0-9的10个数字和a-z的26个字母,然后通过slice截取。比如slice(2,8)就是一段包含数字和字母的长度为6的随机字符串。
通过get移除constructor中的属性定义
通常定义属性是在constructor里面,初始化的时候设置一系列属性的值,这样会导致constructor很多逻辑,而且有的初始化时可以延后的。
通过把属性的定义方式改成get,可以使得constructor更简洁,同时还可以延迟初始化,在访问时才做初始化。
上面的就是我学到的一些知识点,这些api、技巧、编程思路可以帮助我们写出更好的代码。它山之石,可以攻玉。通过学习别人的代码,可以让自己写出更好的代码。
作者:凌霄光
https://juejin.im/post/5e46affdf265da570a5d3d43
❤️ 看完三件事
大家好,我是Jack,如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个【在看】,或者分享转发,让更多的人也能看到这篇内容
关注公众号【前端开发博客】,不定期分享原创&精品技术文章。
添加微信【 kujian89 】。加入前端开发博客公众号交流群。
“在看转发”是最大的支持