【问题标题】:Javascript: object copy, global vars, and performanceJavascript:对象复制、全局变量和性能
【发布时间】:2011-10-05 15:09:53
【问题描述】:

我有一个非常复杂的问题要问:)

我目前正在开发一个 html5 画布游戏。特定于游戏地图的变量位于单独的文件中(我们称之为 game.js),与游戏引擎(我们称之为 engine.js)分开。

我读到在 JS 中使用全局变量比使用局部变量要慢。因此,在 game.js 中,我创建了一个包含所有游戏特定变量的全局变量。在engine.js 中,我将此全局对象复制到局部变量中,然后删除此全局对象。

这是有效的。但我知道分配对象只会传递对这些对象的引用。

因此我的问题是:当我在初始化结束时删除全局对象时,它的性能是否就像我在engine.js中直接将所有变量声明为局部变量一样,还是会更慢,好像我在 engine.js 中的局部变量只是对全局对象的引用?

我可以在 engine.js 中将所有变量声明为本地变量,但如果以后我想制作其他地图/游戏,分离特定于地图的内容对我很有用。

例如:

game.js:

Game = function() {
this.x = 16;
this.y = 16;
this.myObject = {"test": "hello", "action": "none"};
}
game = new Game();

engine.js: //...

var x = game.x;
var y = game.y;
var obj = {"test": game.myObject.test, "action": game.myObject.action};

//...

在这个例子中,x、y 和 obj 的性能会和局部变量一样快,还是更慢?

注意:我并没有真正检查全局变量和本地变量的性能之间的差异,但我认为我读到的内容是正确的。

希望我的问题足够清楚,而不是愚蠢:) 如果您有任何想法...谢谢!

【问题讨论】:

  • 别担心。性能差异微乎其微。
  • 为什么不简单地在engine.js 中实例化Game?无论哪种方式,您都不需要删除全局对象来获得这种性能提升。详情见我的回答。

标签: javascript performance global-variables local-variables


【解决方案1】:

在现代浏览器中,局部变量和全局变量之间可能没有太大的性能差异。

但是,内存消耗存在问题 - 只要页面保持打开状态,全局变量就会持续存在,而局部变量在控制权离开其范围后为 garbage collected。使用局部变量会减少出现memory leak 的机会。

更新

评论线程中的冗长讨论促使我编写了一个测试脚本来测量对比全局和本地范围访问的执行速度差异。

最初似乎没有区别,结果各不相同,没有具体的偏好。然而@DaveNewton 提供了一个反例,它始终显示局部变量的性能提高了一个数量级。


Firefox 7

20000 次全局访问所用的毫秒数:132

用于 20000 次本地访问的毫秒数:159

Internet Explorer 9

20000 次全局访问所用的毫秒数:1750

用于 20000 次本地访问的毫秒数:1699

谷歌浏览器 14

用于 20000 次全局访问的毫秒数:46

用于 20000 次本地访问的毫秒数:55

测试脚本本身

<html>
<head>
<script type="text/javascript">

function t() {

var test = function () {}
test.issue = new String("hello");

var start = new Date().getTime();
(function() {
    (function() {
        (function() {
            (function() {
                (function() {
                    (function() {
                        (function() {
                            var a = document.getElementById("a");
                            for (var i = 0; i < 20000; i++) {
                                a.innerHTML = test.issue.toString();
                            }
                            a = null;
                        })();
                    })();
                })();
            })();
        })();
    })();
})();
var stop = new Date().getTime();
document.getElementById("a").innerHTML = "Milliseconds used for 20000 global accesses: " + (stop - start);


var start = new Date().getTime();
(function() {
    (function() {
        (function() {
            (function() {
                (function() {
                    (function() {
                        (function() {
                            var c = document.getElementById("c");
                            var testx = {};
                            testx.issue = new String("hello");
                            for (var i = 0; i < 20000; i++) {
                                c.innerHTML = testx.issue.toString();
                            }
                            c = null;
                        })();
                    })();
                })();
            })();
        })();
    })();
})();
var stop = new Date().getTime();
document.getElementById("c").innerHTML = "Milliseconds used for 20000 local accesses: " + (stop - start);

}

window.onload = function () {
    document.getElementById('b').onclick = t;
}

</script>
</head>
<body>
<div align="center"><button id="b">Run Test</button></div>
<div id="a"></div>
<div id="c"></div>
</body>

</html>

一个反例,展示了对局部变量的更快访问。

var t0 = new Date();
var i; 
for (i=0; i<10000000; i++); 
var t1 = new Date(); 
function count() { for (var i=0; i<10000000; i++); } 
var t2 = new Date(); 
count(); 
var t3 = new Date(); 
d = document; 
d.write('Without local variables = ', t1-t0, '<br />'); 
d.write('With local variables = ', t3-t2, '<br />');

【讨论】:

  • 这可能并不完全正确,因为 javascript 必须通过原型链递归,直到找到您在全局级别使用的变量。如果您阅读 O'Reilys “Javascript 模式”,他们总是建议将您的全局变量(如果您必须首先拥有全局变量)存储到局部变量中。
  • @Keith.Abramo - ECMA-262 specification 中提到过吗?我以前没有听说过原型链递归。
  • @Saul stackoverflow.com/questions/2895407/…nczonline.net/blog/2009/02/10/javascript-variable-performance 等。范围链很重要! (是否显着差异是一个不同的问题;)
  • @DaveNewton - 我可能错了,但我的印象是范围解析是特定于浏览器的细节。在执行上下文方面,有两种可能的方法——每个堆栈帧都包含所有相关的引用或动态解析。前者显然性能更好,但后者在节省内存方面更胜一筹。
  • @Saul 当然是这样,但如果它是一个全局的 AFAICT,你仍然必须在初始查找中受到打击。你编辑了。是的,您可以始终创建一个包含所有可能的变量查找的框架,但是在一般情况下,IMO 会对性能造成更大的影响——如果有任何引擎这样做,我会感到惊讶。
【解决方案2】:

您所说的性能差异的原因在于 javascript 将变量的名称解析为它所引用的值的方式。所以(可忽略不计的)时间滞后是 javascript 查找变量的结果。当通过引用某物来分配某物时,它是指内存中的实际值而不是名称。

例如,假设您有一个名为info 的javascript 变量,其值为{ question: null, answer: 42 }。如果你要完成任务

info2 = info;

您是在告诉 javascript立即找到该值,并在引用 info2 时使用它。现在infoinfo2 都指向相同的值(相同的内存地址)。您告诉 javascript 查找引用 info 并在每次使用 info2 时获取该值。这意味着您执行此操作的方式很好,因为您只查找全局变量引用一次。

请注意,javascript 只会通过引用分配非常数,所以:

var a = 1;
var b = a;
var a = 2;
var b === 1; // => true

var z = { a: 1 };
var y = z;
z.a = 2;
y.a === 2; // => true

原始值是常量,对象不是。

【讨论】:

    猜你喜欢
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 2019-06-05
    • 2013-01-06
    • 1970-01-01
    • 2019-08-26
    • 2016-12-27
    相关资源
    最近更新 更多