Javascript 分析之谜 - 闭包变量

Javascript profiling mystery - closure variables

如果在闭包内定义变量,我正在测试案例的性能(使用 chrome 时间线)。所以它的值不会暴露给用户。

正如预期的那样run_proto_fn 运行 快了几倍,垃圾回收最少,内存堆低。

但是 run_proto_obj 恰好相反,好像在对象原型 属性 属性中具有非函数值是昂贵的。

有人可以在这里分享一些清晰的信息吗?

SOME = function(){};
SOME.prototype.exe = function(v){
 var x = {
  a:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
  b:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
  c:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
 };
 return x[v];
};

SOME2 = function(){};
SOME2.prototype.exe = function(v){
 return this.exes[v];
};
SOME2.prototype.exes = {
 a:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
 b:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
 c:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
};
SOME_FN = function(){};
SOME_FN.prototype.exe = function(v){
 var x = {
  a: function(p){this.p1 = p;return this;},
        b:function(p){this.p2 = p*3;return this;},
        c:function(p){this.p3 = p*99;return this;},
    };
  return x[v].call(this,42);
};

SOME_FN2 = function(){};
SOME_FN2.prototype.exe = function(v){
    return this.exes[v].call(this,42); 
};
SOME_FN2.prototype.exes = {
    a: function(p){this.p1 = p;return this;},
    b:function(p){this.p2 = p*3;return this;},
    c:function(p){this.p3 = p*99;return this;},
};

var a1 = a2 = a_fn1 = a_fn2 = [];
 

var run_local_obj = function(){
 for (var i = 1000000 - 1; i >= 0; i--) {
  x1 = new SOME();
  x1.exe('a');
  a1.push(x1);
 }
};
var run_proto_obj = function(){
 for (var i = 1000000 - 1; i >= 0; i--) {
  x2 = new SOME2();
  x2.exe('a');
  a2.push(x2);
 }
};
var run_local_fn = function(){
 for (var i = 1000000 - 1; i >= 0; i--) {
  x1 = new SOME_FN();
  x1.exe('a');
  x1.exe('b');
  x1.exe('c');
  a_fn1.push(x1);
 }
};
var run_proto_fn = function(){
 for (var i = 1000000 - 1; i >= 0; i--) {
  x2 = new SOME_FN2();
  x2.exe('a');
  x2.exe('b');
  x2.exe('c');
  a_fn2.push(x2);
 }
};
<button onclick="run_local_obj(this)">local obj</button>
<button onclick="run_proto_obj(this)">proto obj</button>
<button onclick="run_local_fn(this)">local obj FN</button>
<button onclick="run_proto_fn(this)">proto obj</button>


我听过一句话:

closure variable is defined each time that function runs

但是,我还是有雾。

我尝试 运行 测量,首先我注意到的是,仅使用您问题中的代码很难理解发生了什么。

如果我 运行 一个接一个地进行这些测试(单击按钮并查看时间轴),那么接下来的每个 运行 结果都会大不相同。 从少数 运行 看来,第三个 (run_local_fn) 通常比其他的长。

然后我尝试按倒序 运行 它们(单击按钮从 4 到 1)并得到完全不同的结果 - run_local_obj 是最长的。

所以我稍微修改了测试代码,以获得稳定的结果。

完整代码is here,您可以克隆存储库并在本地进行测试。

以下是我 运行 在 Chrome 中的每个测试(我使用 js-perf-test 文件夹内的 python -m SimpleHTTPServer 8082 启动它):

  • 使用 http://localhost:8082/perf.html 打开选项卡,打开开发工具
  • 开始时间线录制
  • 点击按钮
  • 在控制台中等待结果 (min/max/mean)
  • 停止时间线记录

结果稳定且可重复(计时和时间线视图),这是测试之一:

  • `run_local_obj' - 508.45 毫秒,timeline
  • `run_proto_obj' - 433.11 毫秒,timeline
  • `run_local_fn' - 756.26 毫秒,timeline
  • `run_proto_fn' - 560.62 毫秒,timeline

那么我们在这里看到的是:

1) run_proto_obj最快,run_local_obj接近

我认为 run_proto_obj 应该是最有效的,因为它使用带有字符串的静态定义对象。 而JS引擎大概可以优化run_local_obj,所以x对象是重复使用的,并不是每次都创建。

2) run_local_fn 是最慢的。

这里我们有本地 x 对象和动态字符串计算,比其他测试多 floating 部分。

3) run_proto_fnrun_local_fn 快,但比前两个函数慢。

我觉得这也是预料之中的,带a, b, c的对象只定义了一次(所以比run_local_fn快)。

并且与前两个函数相比,它动态计算结果字符串,因此速度较慢。

回到你的问题:

As expected run_proto_fn run few times faster and with minimal garbage collections, and low memory heap. But run_proto_obj happened to make exact opposite, as if it was costly having non-function values at object prototype property properties.

我觉得你只是没有正确设置实验。 根据上面的结果,run_proto_obj其实是最快的。

更新:我刚刚在 Firefox 中尝试了相同的测试,结果相似:

  • `run_local_obj' - 344.85 毫秒
  • `run_proto_obj' - 151.47 毫秒
  • `run_local_fn' - 788.08 毫秒
  • `run_proto_fn' - 265.21 毫秒

run_proto_obj最快,run_local_fn最慢