KB-Javascript Anonymous Function Memory Issue
3 | 9,437 |
過去撰寫Javascript事件,我傳統的寫法會宣告一個事件函數,再把它掛到對應的元素上,例如:(以ASP.NET AJAX Client Library為例)
排版顯示純文字
<input type="button" id="btn" value="Click!" />
<script type="text/javascript">
$addHandler($get("btn"), "click", btnClick);
function btnClick(evt)
{
alert(evt.target.value);
}
</script>
自從開始使用jQuery,範例看多了,我也開始習慣用Anonymous Function的方式宣告事件函數,例如:
排版顯示純文字
$("#btn").click(function() {
alert(this.value);
});
這種寫法可以省去想函數名稱的困擾(老是花時間去想btnClick之類沒營養的名字,常覺得在浪費生命),更大的好處是事件函數本體就接在設定事件程式碼的下方,閱讀時不必上下Scroll尋覓(Visual Studio 2008雖有Javascript Intellisense, 畢竟無法像C#/VB.NET一樣用Goto Definition直接跳到函數所在),甚為方便。但我一直有個疑問: 如果$("#tbl td").click(function() {....});一口氣為數百個td加上Click事件,會不會同樣的函數程式碼就複製了上百份? 白白耗用了不必要的記憶體。
疑問在心中壓了很久,總算動手寫了一小段Code來驗證。
程式裡分別用匿名函數及具名函數的方式為一個div重覆掛上click事件3萬次,藉此觀察記憶體的使用狀況。
排版顯示純文字
<html><head>
<title>Memory Usage Test</title>
<style type="text/css">
.clsDiv
{ cursor: pointer; width: 300px; background-color: blue; color: white; }
</style>
<script type="text/javascript" src="lib/jquery.js"></script>
</head><body>
<script type="text/javascript">
$(function() {
var count = 30000;
$("#dv1").click(function() {
for (var i=0; i < count; i++)
{
$("#dv3").click(function() {
var s =
"01234567890123456789...用大量文字填充...01234567890123456789" +
//重覆多次
"01234567890123456789...用大量文字填充...01234567890123456789" +
});
}
$("#dv3").html("dv1 Clicked!");
});
$("#dv2").click(function() {
for (var i=0; i < count; i++)
$("#dv3").click(fatFunc);
$("#dv3").html("dv2 Clicked!");
});
});
function fatFunc() {
var s =
"01234567890123456789...用大量文字填充...01234567890123456789" +
//重覆多次
"01234567890123456789...用大量文字填充...01234567890123456789" +
}
</script>
<br /><div id="dv1" class="clsDiv">Anonymous Function</div>
<br /><div id="dv2" class="clsDiv">Named Function</div>
<br /><div id="dv3" class="clsDiv">Sheep</div>
</body>
以Task Manager觀察iexplorer.exe佔用記憶體大小當作指標,實驗結果:
測試名稱 | 執行前記憶體用量(KB) | 執行後記憶體用量(KB) |
Anonymous Function | 54,024 | 83,144 |
Named Function | 54,040 | 59,404 |
【結論】反覆宣告Anonymous Function的確會耗用較多的記憶體,但衡量其方便性及可閱讀性,仍可視需要使用。唯需注意較極端的例子(Client Memory極度匱乏、函數本身很擁腫、重覆宣告次數過高...),應調整成具名函數的寫法,以避免記憶體耗用過度影響效能。
Comments
# by rosbicn
这个不一定是真的占用了内存。 Javascript对于Block的处理不同于VB或者C/C++,一个函数的内部变量使用的内存空间不会在函数结束的时候释放,而是等到Gabage Collect的时候才进行回收。所以你观测到的可能是还没有进行Gabage Collect时候的情况。
# by Jeffrey
To rosbicn, 同意您所提的GC理論。 不過我想匿名函數會因記憶體耗用而影響效能仍值得注意。理由是反覆配置記憶體及GC都要耗用CPU Resource;而這個例子為了省事,測試是"重覆"掛在同一個DIV的onclick事件上,若事件是掛在Table裡上千個<td>上,只要Table仍存在,這些事件函數的記憶體就不能被回收。以上是我的一點補充,請參考。
# by rosbicn
To Jeffrey,你说的绝对正确。如果是挂在Table上千个td上,所有的内存绝对都不能回收。我猜想如果你例子是这么写的话,那测试出来的内存消耗可能会远大于你文章中的结果。的确,用Table中上千个td做例子,你的文章更有说服力。