在学习 React 事件系统的时候,在事件分发的 dispatch方法发现了调用了一个 pooledClass 方法,一时半会没看明白这个方法的用意。数据库
咱们先看一下是怎么用的:segmentfault
// step1 function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) { this.topLevelType = topLevelType; this.nativeEvent = nativeEvent; this.ancestors = []; } Object.assign(TopLevelCallbackBookKeeping.prototype, { destructor: function() { this.topLevelType = null; this.nativeEvent = null; this.ancestors.length = 0; }, }); PooledClass.addPoolingTo( TopLevelCallbackBookKeeping, PooledClass.twoArgumentPooler ); // step2 var bookKeeping = TopLevelCallbackBookKeeping.getPooled( topLevelType, nativeEvent ); // bookKeeping 是 TopLevelCallbackBookKeeping 的实例 try { // Event queue being processed in the same cycle allows // `preventDefault`. ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { //释放 TopLevelCallbackBookKeeping.release(bookKeeping); }
那么这里为何不直接 new 一个 TopLevelCallbackBookKeeping, 而要经过这个 PooledClass 来返回 TopLevelCallbackBookKeeping 的实例呢缓存
单例模式是限制了一个类只能有一个实例,对象池模式则是限制一个类实例的个数。对象池类就像是一个对象管理员,它以Static列表(也就是装对象的池子)的形式存存储某个实例数受限的类的实例,每个实例还要加一个标记,标记该实例是否被占用。当类初始化的时候,这个对象池就被初始化了,实例就被建立出来。而后,用户能够向这个类索取实例,若是池中全部的实例都已经被占用了,那么抛出异常。用户用完之后,还要把实例“还”回来,即释放占用。对象池类的成员应该都是静态的。用户也不该该能访问池子里装着的对象的构造函数,以防用户绕开对象池建立实例。书上说这个模式会用在数据库链接的管理上。好比,每一个用户的链接数是有限的,这样每一个链接就是一个池子里的一个对象,“链接池”类就能够控制链接数了。
若是说每次触发dispatch
的时候都用 new TopLevelCallbackBookKeeping 来 new 一个对象,那么当触发不少次dispatch
的时候,就会致使生成多个对象没法销毁(多个bookKeeping的引用次数一直为1),致使内存溢出。
对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不当即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用,从而减小建立对象和释放对象的次数,进而改善应用程序的性能。事实上,因为对象池技术将对象限制在必定的数量,也有效地减小了应用程序内存上的开销。函数
将用过的对象保存起来,等下一次须要这种对象的时候,再拿出来重复使用,从而在必定程度上减小频繁建立对象所形成的开销。React 的 pooledClass.js 就是一个例子:性能
var invariant = require('invariant'); /** * Static poolers. Several custom versions for each potential number of * arguments. A completely generic pooler is easy to implement, but would * require accessing the `arguments` object. In each of these, `this` refers to * the Class itself, not an instance. If any others are needed, simply add them * here, or in their own files. */ var oneArgumentPooler = function(copyFieldsFrom) { var Klass = this; if (Klass.instancePool.length) { var instance = Klass.instancePool.pop(); Klass.call(instance, copyFieldsFrom); return instance; } else { return new Klass(copyFieldsFrom); } }; ... var standardReleaser = function(instance) { var Klass = this; invariant( instance instanceof Klass, 'Trying to release an instance into a pool of a different type.' ); instance.destructor(); if (Klass.instancePool.length < Klass.poolSize) { Klass.instancePool.push(instance); } }; var DEFAULT_POOL_SIZE = 10; var DEFAULT_POOLER = oneArgumentPooler; /** * Augments `CopyConstructor` to be a poolable class, augmenting only the class * itself (statically) not adding any prototypical fields. Any CopyConstructor * you give this may have a `poolSize` property, and will look for a * prototypical `destructor` on instances (optional). * * @param {Function} CopyConstructor Constructor that can be used to reset. * @param {Function} pooler Customizable pooler. */ var addPoolingTo = function(CopyConstructor, pooler) { var NewKlass = CopyConstructor; NewKlass.instancePool = []; NewKlass.getPooled = pooler || DEFAULT_POOLER; if (!NewKlass.poolSize) { NewKlass.poolSize = DEFAULT_POOL_SIZE; } NewKlass.release = standardReleaser; return NewKlass; }; var PooledClass = { addPoolingTo: addPoolingTo, oneArgumentPooler: oneArgumentPooler, twoArgumentPooler: twoArgumentPooler, threeArgumentPooler: threeArgumentPooler, fourArgumentPooler: fourArgumentPooler, fiveArgumentPooler: fiveArgumentPooler, }; module.exports = PooledClass;
具体分为三步学习
说的再简单一点就是ui
并不是全部对象都适合拿来池化――由于维护对象池也要形成必定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能下降的状况。可是对于生成时开销可观的对象,池化技术就是提升性能的有效策略了。this