与问题有关的两个jsperf:
Cache-ing 'this'
Cache-ing booleans
我在Mac 10.9上。在Safari 7,Chrome 32和Firefox 26中,将“this”存储在变量中似乎比不存储变量要慢一些。例如:
function O() {
var THIS = this;
THIS.foo = 'foo';
THIS.bar = 'bar';
THIS.baz = 'baz';
}
有点慢:
function O() {
this.foo = 'foo';
this.bar = 'bar';
this.baz = 'baz';
}
这是为什么?是因为'this'每次引用原始对象吗?
在Chrome和Firefox中,存储一个布尔对象然后再引用该变量的值似乎比每次写入“true”或“false”要快一些(理论上,每次创建一个新的布尔对象)。但是,在Safari中,相反的情况似乎是正确的。例如:
function lt() {
if(arguments[0] < arguments[1]) return true;
return false;
}
比Firefox更快(在Firefox和Chrome中):
var TRUE = true,
FALSE = false;
function lt() {
if(arguments[0] < arguments[1]) return TRUE;
return FALSE;
}
除了Safari之外,这是因为每次不在变量中存储时都会创建一个新的Boolean对象吗?什么可以解释为什么在Safari中有相反的效果?
我倾向于认为在一小段代码中,差异可以忽略不计,但我很好奇当代码变得更长时它是否会引起关注。我还阅读了一个询问性能数据与感知性能的问题,在这些情况下,通常需要考虑感知性能。
jsperfs中上述统计信息的一个问题是缺少大量数据样本。我问这个问题的原因是因为我正在写一个小的JS库。在这种情况下,就缓存某些对象而言,最佳做法是什么?
答案 0 :(得分:0)
通常,对象在对象图中越深,就越有理由对其进行缓存。因此,例如:没有理由在您的示例中缓存this
,但如果您有一个对象foo.bar.something.else.x
并且想要多次使用,那么您最好将其缓存到局部变量中。它会更快,也更具可读性。
如果对象链中存在多个级别,则缓存对象的另一个好理由。例如,当你有4级嵌套函数时,在最里面你想多次使用全局作用域中的变量,将它缓存在局部变量中是合理的。
答案 1 :(得分:0)
在这些示例中,很难回答关于为什么safari与Firefox和Chrome的行为不同的问题,因为其原因非常依赖于实现,而且我不熟悉这些浏览器的源代码。我也没有花很长时间试图对它们进行逆向工程。但是我可以粗略地描述变量缓存如何仅使用ECMAScript规范影响性能:
首先,了解 this 关键字在调用期间初始化是很重要的,并且它始终是执行上下文的本地关键字,即this关键字的查找将在激活记录中终止。当前函数调用。因此,在您的第一个示例中,您将创建一个额外的局部变量 - 将工作加倍 - 将执行上下文留下以下激活记录(省略其他系统定义的属性,例如参数):
{ this: caller, THIS: caller };
// this: system-created property (?during function object creation or invocation?)
// caller: system-initialized value, during invocation
// THIS: user-created-and-initialized property, slows down execution
因此,一般来说,缓存变量的最佳原因是因为查找操作比创建本地属性更昂贵。创建本地属性显然只会在多次引用时提高性能。前面提到的操作可以在范围链上完成,或者使用虚线查找操作在原型链上完成。只要属性创建比实际查找操作便宜,就可以缓存属性。
缓存值的最佳原因,是为了避免为每次调用反复创建相同的值。缓存值的最佳方式是通过(匿名)闭包。 E.g:
var obj0, obj1, funExt;
obj0 = {};
obj1 = {};
funExt = (function () {
var cachedId, cachedObj;
// After evaluation of funExt is done these values will persist in closure,
// avoiding value creation during the execution of returned function.
cachedId = "extension";
cachedObj = { prop0: myValue, prop1: myValue, prop2: myValue };
return function (o) {
if (o) {
o[cachedId] = cachedObject;
} else if (this !== window) {
o = (this[cachedId] = cachedObj);
}
return o;
};
}());
obj0.funExt();
funExt(obj1);
关于你的第二个例子,我只能说在Chrome和Firefox中,布尔创建比单级范围查找要便宜。鉴于示例函数的复杂性,两个操作都应该相当便宜。
还要记住,开发人员设计javascript引擎以便即时进行这些在线优化。因此无法保证这些优化会带来显着的性能提升。在构建库时,我更倾向于坚持简洁和清晰的结构。所以我使用闭包来传达正在共享的值,或者某些值保持不变。如果给定变量太长或缺乏清晰度,我会使用变量缓存。