Closed lianqin7 closed 11 years ago
其实也不然,如果继承链比较长,extend 会调用多遍,每次 extend 都需要去 merge 之前的属性。
这就要看使用场景了,是实例化的数量还是继承链的长短。
而且我觉得 hack class 并不是一个明智的选择。
能否找一个真实场景测试?现在的用例有点极端。
2013/7/8 Haoliang Gao notifications@github.com
其实也不然,如果继承链比较长,extend 会调用多遍,每次 extend 都需要去 merge 之前的属性。
这就要看使用场景了,是实例化的数量还是继承链的长短。
而且我觉得 hack class 并不是一个明智的选择。
— Reply to this email directly or view it on GitHubhttps://github.com/aralejs/class/issues/8#issuecomment-20588495 .
王保平 / 玉伯(射雕) 送人玫瑰手有余香
@popomore
只要有个固定的规则,每次调用extend 只要merge自己和parent(parent的attrs由parent及parent的parent merge而成)的attrs 就ok了,这个调用次数基本上小于实例化的数量。
hack class 当然只是为了验证这个方法的性能提升,还是希望能有更优雅的解决方案
@lifesinger
用例确实极端了点,不过稍微复杂点的功能型页面,有多个 popup,overlay 之类的实例还是很正常的。更别说 validator 这种每个 item 都是一个 widget 实例的场景了。每次都在实例化的时候都去遍历一遍继承链,重复开销很严重。。
或者说换个思路,做第一次实例化的时候对attrs做个缓存,以后每次实例化的去取该缓存,也可以节省不少开销。。
有点奇怪了,理论上做过缓存之后,后面反复实例化某个类的时候 merge 的次数更少了,速度应该更快才是。 测试结果是 IE系列速度提升, chrome和ff性能反而下降了 orz
代码如下:
var attrsCache = {};
var cid = 1;
function mergeInheritedAttrs(attrs, instance, specialProps) {
var inherited = [];
var proto = instance.constructor.prototype;
var attrCid = instance.constructor.__cid;
// 如果 constructor 存在缓存,取缓存
if (attrCid && attrsCache[attrCid]){
mergeAttrs(attrs, attrsCache[attrCid]);
return;
}
while (proto) {
// 不要拿到 prototype 上的
if (!proto.hasOwnProperty('attrs')) {
proto.attrs = {};
}
// 将 proto 上的特殊 properties 放到 proto.attrs 上,以便合并
copySpecialProps(specialProps, proto.attrs, proto);
// 为空时不添加
if (!isEmptyObject(proto.attrs)) {
inherited.unshift(proto.attrs);
}
// 向上回溯一级
proto = proto.constructor.superclass;
}
// Merge and clone default values to instance.
for (var i = 0, len = inherited.length; i < len; i++) {
mergeAttrs(attrs, normalize(inherited[i]));
}
// 缓存在 constructor 上
instance.constructor.__cid = cid;
attrsCache[cid++] = mergeAttrs({}, attrs);
}
cache 不一定能提升性能,之前 Sea.js 里,resolveCache 我加过,但在一般场景下,会降低性能,我当初也是纳闷死了。
后来发现 cache 本身是存在开销的(存储和查询),只有当 cache 本身的开销小于节省的开销时,对性能才有提升,否则不光耗费性能,还多占用了内存。
2013/7/8 lianqin7 notifications@github.com
有点奇怪了,理论上做过缓存之后,后面反复实例化某个类的时候 merge 的次数更少了,速度应该更快才是。 测试结果是 IE系列速度提升, chrome和ff性能反而下降了 orz
代码如下:
var attrsCache = {}; var cid = 1; function mergeInheritedAttrs(attrs, instance, specialProps) { var inherited = []; var proto = instance.constructor.prototype; var attrCid = instance.constructor.__cid;
// 如果 constructor 存在缓存,取缓存 if (attrCid && attrsCache[attrCid]){ mergeAttrs(attrs, attrsCache[attrCid]); return; } while (proto) { // 不要拿到 prototype 上的 if (!proto.hasOwnProperty('attrs')) { proto.attrs = {}; } // 将 proto 上的特殊 properties 放到 proto.attrs 上,以便合并 copySpecialProps(specialProps, proto.attrs, proto); // 为空时不添加 if (!isEmptyObject(proto.attrs)) { inherited.unshift(proto.attrs); } // 向上回溯一级 proto = proto.constructor.superclass; } // Merge and clone default values to instance. for (var i = 0, len = inherited.length; i < len; i++) { mergeAttrs(attrs, normalize(inherited[i])); } // 缓存在 constructor 上 instance.constructor.__cid = cid; attrsCache[cid++] = mergeAttrs({}, attrs);
}
测试结果:http://jsperf.com/arale-attribute-performance
— Reply to this email directly or view it on GitHubhttps://github.com/aralejs/class/issues/8#issuecomment-20592024 .
王保平 / 玉伯(射雕) 送人玫瑰手有余香
@lifesinger
soga,学习了。看来在这里加缓存并没有什么实质性的提升。暂时先放弃这个优化思路了。看看从别的地方去入手
attribute.js ,我这样改怎么样?specialProps 感觉自己用不上就直接注释了 var Class = require("./class");
Class.Mutators["attrs"] = function( attrs ) { var proto = this.prototype;
// 原型链上不存在 attrs 属性
if ( !proto.attrs ) {
proto.attrs = normalize( attrs );
return;
}
var inherited = [];
// 如果 attrs 在父类原型链中
if ( !proto.hasOwnProperty("attrs") ) {
//debugger;
inherited.push( proto.attrs );
proto.attrs = {};
}
inherited.push( attrs );
for ( var i = 0, len = inherited.length; i < len; i++ ) {
mergeAttrs( proto.attrs, normalize( inherited[ i ] ) );
}
};
exports.initAttrs = function(config) { // initAttrs 是在初始化时调用的,默认情况下实例上肯定没有 attrs,不存在覆盖问题 var attrs = {};
// Get all inherited attributes. //var specialProps = this.propsInAttrs || []; //mergeInheritedAttrs(attrs, this, specialProps);
// 原型链上的属性要重新合并, 不需要 normalize ,原型合并时已调用, this.attrs 会指向最近的原型链 mergeAttrs( attrs, this.attrs ); this.attrs = attrs;
// Merge user-specific attributes from config. if (config) { mergeUserValue(attrs, config); }
// 对于有 setter 的属性,要用初始值 set 一下,以保证关联属性也一同初始化 setSetterAttrs(this, attrs, config);
// Convert on/before/afterXxx
config to event handler.
parseEventsFromAttrs(this, attrs);
// 将 this.attrs 上的 special properties 放回 this 上 //copySpecialProps(specialProps, this, attrs, true); };
需求来源于 arale/base 的性能优化:
将 attribute 的 merge 操作由 实例化时 -> 类构建时,可以提升较多的性能
可惜现在的class的extend之后没有接口能保证所有的子类构造时,都能运行同一段功能,且对其他使用者透明( 除了hack Class.create 方法 )
或者说大家有更好的解决方案?
@lifesinger @popomore @afc163