Javascript:对象副本,全局变量和性能

时间:2011-10-04 15:20:33

标签: javascript performance global-variables local-variables

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

我目前正在开发一款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的性能是否与局部变量一样快,还是更慢?

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

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

2 个答案:

答案 0 :(得分:2)

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

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

更新

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

最初似乎没有任何差异,结果会有所不同,对某一方面没有特别的偏好。然而,@ DaveNewton提供了一个反例,它一直表示局部变量的性能提高了一个数量级。


Firefox 7

  

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

     

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

Internet Explorer 9

  

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

     

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

Google Chrome 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 />');

答案 1 :(得分: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

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