JavaScript:对象复制,全局变量和性能

4

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

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

注意:我没有真的检查全局变量和本地变量之间的性能差异,但我假设我所读到的是正确的。

希望我的问题已经足够清晰,而且不会很傻 :) 如果你有任何想法...谢谢!


不用担心,性能差异微不足道。 - SLaks
为什么不在engine.js中实例化“Game”?无论如何,您都不需要删除全局对象来获得此性能提升。有关详细信息,请参见我的答案。 - benekastah
2个回答

2
在现代浏览器中,本地变量和全局变量之间的性能差异可能不大。 然而,存在内存消耗问题 - 全局变量会持续存在于页面打开期间,而本地变量在控制离开其作用域后会被回收。使用本地变量减少了出现内存泄漏的机会。

更新

评论线程中冗长的讨论促使我编写了一个测试脚本,以测量对比全局和本地范围访问的执行速度差异。 最初似乎没有区别,结果也各不相同,没有特定偏好的一方。然而@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 />');

这可能并不完全正确,因为JavaScript必须通过原型链递归查找您在全局级别使用的变量。如果您阅读O'Reilys的"JavaScript Patterns",他们总是建议将全局变量(如果您必须首先使用全局变量)存储到本地变量中。 - Keith.Abramo
@Saul https://dev59.com/dk3Sa4cB1Zd3GeqPzvvT, http://www.nczonline.net/blog/2009/02/10/javascript-variable-performance/, 等等。作用域链很重要!(它是否是一个显著的差异是另一回事;) - Dave Newton
当然是这样的,但如果它是全局的话,你仍然需要承受初始查找的代价。你进行了编辑。是的,你可以创建一个包含所有可能变量查找的框架,但在一般情况下,我认为这会带来更大的性能损失 - 如果有任何引擎这样做,我会感到惊讶。 - Dave Newton
@DaveNewton - 看,问题是关于本地作用域访问是否比全局作用域访问更快。实际上,访问速度似乎是相等的(请参见更新)。 - Saul
@DaveNewton - 视觉美学几乎不是基准的标准,我认为其功能相当相关 - 它比较了来自嵌套作用域的全局访问和本地访问。您可以改进它或贡献更好的替代方案。 - Saul
显示剩余9条评论

2
你所提到的性能差异原因在于javascript解析变量名称并将其指向对应值的方式。因此,(微不足道的)时间延迟是javascript查找变量的结果。当某个东西被引用赋值给另一个东西时,它是与内存中的实际值相关联而不是名称。
例如,假设你有一个javascript变量叫做"info",其值为"{ question: null, answer: 42 }"。如果你进行以下赋值操作:
info2 = info;

你正在告诉JavaScript立即查找该值并在引用info2时使用它。现在,info和info2都指向相同的值(相同的内存地址)。你没有告诉JavaScript每次使用info2时都要查找引用info并获取该值。这意味着你目前的方法是可行的,因为你只查找全局变量引用一次。
请注意,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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接