作者:卡颂,来源:魔术师卡颂
这两年有不少朋友和我吐槽react源码,比如:
作为业务依赖的框架,为了提升一点点运行时性能,React从不吝惜将源码写的很复杂。
在涉及状态、标记位、优先级操作的地方大量使用了位运算。
本文会讲解其中比较有代表性的部分。学到之后,当遇到类似场景时露一手,你就是业务线最靓的仔。
在JS中,位运算的操作数会先转换为Int32(32位有符号整型),执行完位运算会Int32对应浮点数。
在React中,主要用到3种位运算符 —— 按位与、按位或、按位非。
对于两个二进制操作数的每个bit,如果都为1,则结果为1,否则为0。
举个例子,计算3 & 2,首先将操作数转化为Int32:
// 3对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011
// 2对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0010
为了直观,我们排除前面的0,只保留最后8位(实际参与计算的应该是32位):
0000 0011
& 0000 0010
-----------
0000 0010
所以3 & 2计算结果转化为浮点数后为2。
对于两个二进制操作数的每个bit,如果都为0,则结果为0,否则为1。
计算10 | 3:
0000 1010
| 0000 0011
-----------
0000 1011
计算结果转化为浮点数后为11。
对一个二进制操作数的每个bit,逐位进行取反操作(0、1互换)
对于~3,将3转化为Int32后逐位取反:
// 3对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011
// 逐位取反
0b111 1111 1111 1111 1111 1111 1111 1100
计算结果转化为浮点数后为-4。
如果你对这个结果有疑惑,可以去了解补码相关知识
让我们从易到难,看看位运算在React中的应用。
React源码内部有多个上下文环境,在执行函数时经常需要判断当前处在哪个上下文环境中。
假设共有三种上下文情况:
// A上下文
const A = 1;
// B上下文
const B = 2;
// 没有处在上下文
const NoContext = 0;
当进入某个上下文时,可以使用按位或操作标记进入:
// 当前所处上下文
let curContext = 0;
// 进入A上下文
curContext |= A;
我们用8位二进制举例(同样,实际应该是Int32,这里是为了简化),curContext与A执行按位或操作:
0000 0000 // curContext
| 0000 0001 // A
-----------
0000 0001
此时可以结合按位与操作与NoContext来判断是否处在某一上下文中:
// 是否处在A上下文中 true
(curContext & A) !== NoContext
// 是否处在B上下文中 false
(curContext & B) !== NoContext
离开某上下文后,结合按位与、按位非移除标记:
// 从当前上下文中移除上下文A
curContext &= ~A;
// 是否处在A上下文中 false
(curContext & A) !== NoContext
curContext与~A执行按位与操作:
0000 0001 // curContext
& 1111 1110 // ~A
-----------
0000 0000
即从curContext中移除A。
当业务中需要同时处理多个状态时,可以使用如上位运算技巧。
在React中,不同情况下调用this.setState触发的更新会拥有不同优先级。优先级之间的比较、挑选同样使用了位运算。
具体来说,React中用31个bit位保存更新(之所以是31而不是32是因为Int32的最高位是符号位,不保存具体的数)。
处在越低bit位的更新优先级越高(越需要优先处理)。
举个例子,假设当前应用存在2个更新:
0b000 0000 0000 0000 0000 0000 0001 0001
其中第1位的更新优先级最高(需要同步处理),第5位为默认优先级。
React经常需要找出当前最高优先级的更新在哪一位(如上例子中在第一位),方法如下:
function getHighestPriorityLane(lanes) {
return lanes & -lanes;
}
解释下,由于Int32采用补码表示,所以-lanes可以看作如下两步操作:
为了直观,用8位表示:
lanes 0001 0001
~lanes 1110 1110 // 第一步
+1 1110 1111 // 第二步
则lanes & -lanes如下:
0001 0001 // lanes
& 1110 1111 // -lanes
-----------
0000 0001
取到的就是第一位(已有更新中最高的优先级)。
虽然业务中不常使用位操作,但在特定场景下位操作时很方便、高效的方式。
这波操作你爱了么?
在日常 Coding 中,码农们肯定少不了对数组的操作,其中很常用的一个操作就是对数组进行遍历,查看数组中的元素,然后一顿操作猛如虎。今天暂且简单地说说在 JavaScript 中 forEach。
克隆项目代码到本地(git应该都要会哈,现在源码几乎都会放github上,会git才方便,不会的可以自学一下哦,不会的也没关系,gitHub上也提供直接下载的链接);打开微信开发者工具;
随着这些模块逐渐完善, Nodejs 在服务端的使用场景也越来越丰富,如果你仅仅是因为JS 这个后缀而注意到它的话, 那么我希望你能暂停脚步,好好了解一下这门年轻的语言,相信它会给你带来惊喜
在 Vue 内部,有一段这样的代码:上面5个函数的作用是在Vue的原型上面挂载方法。initMixin 函数;可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动
nextTick的使用:vue中dom的更像并不是实时的,当数据改变后,vue会把渲染watcher添加到异步队列,异步执行,同步代码执行完成后再统一修改dom,我们看下面的代码。
React更新的方式有三种:(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)(2)setState(3)forceUpdate;接下来,我们就来看下ReactDOM.render()源码
在React中,为防止某个update因为优先级的原因一直被打断而未能执行。React会设置一个ExpirationTime,当时间到了ExpirationTime的时候,如果某个update还未执行的话,React将会强制执行该update,这就是ExpirationTime的作用。
算法对于前端工程师来说总有一层神秘色彩,这篇文章通过解读V8源码,带你探索 Array.prototype.sort 函数下的算法实现。来,先把你用过的和听说过的排序算法都列出来:
extend是jQuery中一个比较核心的代码,如果有查看jQuery的源码的话,就会发现jQuery在多处调用了extend方法。作用:对任意对象进行扩;’扩展某个实例对象
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!