PooledClass用途
PooledClass在整个react运作流程中用处很多。前面分析各个模块时候都曾遇到过。
例如ReactUpdates.js中
1 | PooledClass.addPoolingTo(ReactUpdatesFlushTransaction); |
例如ReactReconcileTransaction.js中
1 | PooledClass.addPoolingTo(ReactReconcileTransaction); |
例如CallbackQueue.js
1 | module.exports = PooledClass.addPoolingTo(CallbackQueue); |
用到的地方确实不少。有些是事务上,也有一些是Class(这里应该算是一个工厂函数)上。
分析
1 | var addPoolingTo = function<T>( |
这里注意函数传参后的处理就可以了,当CopyConstructor作为参数传入时候会复制一份引用,此时函数内的NewClass和CopyConstructor其实是两个独立的变量,但由于是引用类型所以指向了同一份引用,所以当修改时候这个引用会被修改,这样最终会导致CopyConstructor引用的变量也会变化。这就是call by sharing。
这个函数是有返回值的,但是这里需要注意这个返回值最后和CopyConstructor引用了同一份引用,所以捕获不捕获这个返回值其实没有任何区别。
当然addPoolingTo只是一个简单的加工函数,它给CopyConstructor添加了一个属性两个方法。
属性or方法 | 说明 |
---|---|
instancePool | Array 实例池 |
getPooled | Method 从实例池获取实例 |
release | Method |
默认情况下NewKlass.getPooled指向oneArgumentPooler
1 | var oneArgumentPooler = function (copyFieldsFrom) { |
此时oneArgumentPooler作为NewKlass方法指向NewKlass本身。
这个函数的意义在于,会对instancePool做检查,如果instancePool(实例池)里面有东西那么就把最后一个拿出来用,否则新new一个实例。
release指向了standardReleaser
1 | var standardReleaser = function (instance) { |
它会调用CopyConstructor.destructor方法,然后将Klass压到对象池内。一般来说此时Klass就是一个空白的实例。当后面哪一次再次调用getPooled它会直接从对象池中返回这个实例而不去new新的返回。
以CallbackQueue为例。首先它返回一个CallbackQueue构造器,它的原型上有destructor方法。
当执行addPoolingTo后,它就加上了instancePool、getPooled、release
属性或者方法。
第一次执行getPooled它会new CallbackQueue构造器(此处设赋值给instance)。如果在这之后再次对这个实例执行CallbackQueue.release(instance),那么就会被推入到对象池,下次再次getPooled就会直接从对象池里面获取这个实例。
思考
那么现在的问题很明显,究竟是为什么要做这个机制呢?
观察这个PooledClass包装后的结果的引用位置,会发现基本都是在事务环节。而事务环节有大量的清场、还原现场的需求,而它本身更是存在被频繁触发的可能。所以这里就有了一个缓存的需要。这是PooledClass存在的意义所在。
为了印证这个想法,我把批量更新到函数找了出来 flushBatchedUpdates(ReactUpdates.js)
1 | var flushBatchedUpdates = function() { |
很显然,这里对dirtyComponents进行了遍历,如果没有对象池处理,每次遍历过程中的更新都会重新new一个事务,然后重新销毁。但是这里有了事务之后就可以直接getPooled()用完之后release,这样对象池里面就是一个初始化状态的实例。提到初始化这个过程显然跑不了查看一下事务上的destructor(transaction.release环节会执行这个函数):
1 | destructor: function() { |
对照一下其构造器:
1 | function ReactUpdatesFlushTransaction() { |
很容易发现这两个反向操作。很有意思的是他们里面也有一个CallbackQueue的getPooled和release过程。
总结
getPooled其实是基于React事务需要频繁new和销毁的需要构建的抽象对象池逻辑,以达到节省性能和内存支出的目的。