从前端源码中学到的知识点

在看一些优秀的源码的过程中,经常能学到一些新的东西。一些没用过的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 】。加入前端开发博客公众号交流群。

“在看转发”是最大的支持

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值