前段时间被IE和JavaScript脚本引擎的Memory Leak问题弄得郁闷坏了,不过幸好现在总算是
柳暗花明了,并且找到了一些IE中使用脚本避免ML问题的方法。继续研究JavaScript的编写,有发现一些不算ML问题,但是可以节约IE内存使用的方法,在此和大家讨论讨论。
我们在JavaScript中编写代码,对于定义函数的语句:
function
foo()
{
//
TODO: . . .
return
x;
}
可以说是在熟悉不过了。当然除了这种定义函数的方法,我们还有另外几种方法也能定义函数:
var
foo
=
function
()
{
//
TODO: . . .
return
x;
}
var
foo
=
new
Function('{
/*
todo
*/
return
x;}');
后两种方法定义的JavaScript函数,在调用起来和第一种没有任何效果上的区别。
不过由于JavaScript是解释性语言,当我们定义一个函数的时候,解析引擎生成一个Function对象实例,然后把函数内容保存下来。所以每执行一次函数定义语句,就会生成一个函数。而不像编译语言,一个函数编译一次后就被任何语句调用。啊?难道JavaScript不能调用定义好的函数?不是这个意思了,当我们在制作JavaScript控件时,如果动态输出DHTML来作为控件的内容,就容易出现这样的问题。比如我们在一个HTML对象生成过程中,使用了inline方式定义的函数,那么这个元素生成几次,那个函数也就要同时生成几次。
function
TestObject.prototype.Render(doc, id)
{
var
span
=
doc.createElement('SPAN');
span.Object
=
this
;
this
.m_Element
=
span;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
if
( id
==
"
NamedMethod
"
)
{
span.onclick
=
asdf;
}
else
{
span.onclick
=
function
()
{
var
asdf01
=
['a', 's', 'd', 'f'];
var
asdf02
=
['a', 's', 'd', 'f'];
var
asdf03
=
['a', 's', 'd', 'f'];
var
asdf04
=
['a', 's', 'd', 'f'];
var
asdf05
=
['a', 's', 'd', 'f'];
var
asdf06
=
['a', 's', 'd', 'f'];
var
asdf07
=
['a', 's', 'd', 'f'];
var
asdf08
=
['a', 's', 'd', 'f'];
var
asdf09
=
['a', 's', 'd', 'f'];
var
asdf10
=
['a', 's', 'd', 'f'];
var
asdf11
=
['a', 's', 'd', 'f'];
var
asdf12
=
['a', 's', 'd', 'f'];
};
}
span.Name
=
this
.m_Description;
span.innerText
=
this
.m_Name;
span.style.display
=
'block';
return
span;
}
函数span.onclick = function()中的内容是用来占位置的,这样inline方式定义函数,每次Render()都就会生成一个新的函数对象。使用inline方式有什么不好呢?当对象实例多了的时候,会很明显的浪费内存空间呀,试验数据如下:
| Normal Method | Inline Method |
---|
|
Initialized | 27.4 M | 27.4 M |
Rendered | 33.4 M | 35.2 M |
// IE消耗的内存数量(PM+VM)
单看绝对内存消耗差别不大,可是如果看相对内存消耗:(35.2-33.4)/(33.4-27.4) =
30% !!!,还是很可观的了,而且如果方法本省越大,inline时冗余数据就越多。
附测试代码:
<
html
>
<
head
>
<
title
>
JScript Function Spending
</
title
>
<
meta
name
="author"
content
="birdshome@博客园"
/>
</
head
>
<
body
onunload
="ReleaseElements()"
>
<
button
id
="NamedMethod"
onclick
="GenerateObjects(this)"
>
Append Normal Elements
</
button
>
<
button
id
="AnonymousMethod"
onclick
="GenerateObjects(this)"
>
Append Inline Elements
</
button
>
<
div
id
="container"
>
</
div
>
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
<
script
language
="Javascript"
>
function GenerateObjects(elmt)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
{
var room = document.getElementById('container');
for ( var i=0 ; i < 1000 ; ++i )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
var obj = new TestObject('__Object__' + i);
room.appendChild(obj.Render(document, elmt.id));
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
function TestObject(name)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
{
this.m_Name = name;
this.m_Description = '';
this.m_Element = null;
this.toString = function()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
return '[class TestObject]';
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
function TestObject.prototype.Render(doc, id)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
{
var span = doc.createElement('SPAN');
span.Object = this;
this.m_Element = span;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
if ( id == "NamedMethod" )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
span.onclick = asdf;
}
else
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
span.onclick = function()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
var asdf01 = ['a', 's', 'd', 'f'];
var asdf02 = ['a', 's', 'd', 'f'];
var asdf03 = ['a', 's', 'd', 'f'];
var asdf04 = ['a', 's', 'd', 'f'];
var asdf05 = ['a', 's', 'd', 'f'];
var asdf06 = ['a', 's', 'd', 'f'];
var asdf07 = ['a', 's', 'd', 'f'];
var asdf08 = ['a', 's', 'd', 'f'];
var asdf09 = ['a', 's', 'd', 'f'];
var asdf10 = ['a', 's', 'd', 'f'];
var asdf11 = ['a', 's', 'd', 'f'];
var asdf12 = ['a', 's', 'd', 'f'];
};
}
span.Name = this.m_Description;
span.innerText = this.m_Name;
span.style.display = 'block';
return span;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
function asdf()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
{
var asdf01 = ['a', 's', 'd', 'f'];
var asdf02 = ['a', 's', 'd', 'f'];
var asdf03 = ['a', 's', 'd', 'f'];
var asdf04 = ['a', 's', 'd', 'f'];
var asdf05 = ['a', 's', 'd', 'f'];
var asdf06 = ['a', 's', 'd', 'f'];
var asdf07 = ['a', 's', 'd', 'f'];
var asdf08 = ['a', 's', 'd', 'f'];
var asdf09 = ['a', 's', 'd', 'f'];
var asdf10 = ['a', 's', 'd', 'f'];
var asdf11 = ['a', 's', 'd', 'f'];
var asdf12 = ['a', 's', 'd', 'f'];
}
</
script
>
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
<
script
language
="javascript"
>
function ReleaseElements()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
{
var room = document.getElementById('container');
var spans = room.all.tags('SPAN');
for ( var i=0 ; i < spans.length ; ++i )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
spans[i].Object = '';
}
}
</
script
>
</
body
>
</
html
>
题外话, 我感觉 this.m_Element = span;
这个命名不妥。
m_xxx 通常用于命名私有的成员变量,即 field.
而通过 this 声明的变量或者函数是作为公有的属性或方法暴露的。类似于 C# 中的 property. 可能直接大写会好些,不至于造成混淆。比如:
function T() {
var m_x = 5;
this.Name = 'xxx';
this.Password = '...';
...
}
为什么在JavaScript中这么命名,我是这么考虑的:
一、我的希望外部调用的方法都是大写字母开头的,和.NET Framework一样;
二、不希望外部调用的方法,用'__[a-z]'开头;
三、对于类中的成员属性我使用m_开头;
四、对于非成员属性,以camel style的形式命名;
五、事件以'e_'开头。
关于外部访问m_开头的变量,这是由于JavaScript成员没有private一说,而且脚本本身源代码都有,private也没有意义嘛。所以m_只是一个标记,为了和方法区分。
嵌套函数不是inline函数,楼主不要乱用术语。js中的嵌套函数实际上就是closure。
BTW,js的命名规范应多接近java而不是C#。如果以此为标准,则你的好几条都违背了:
第一,方法始终以小写字母开头。
第二,内部成员以_开头,一个下划线即可。
第三,不要用m_前缀,也用一个下划线即可,因为没有必要区分成员属性和成员方法。
第五,事件无需特别命名,C#也没有特殊命名规范,或者可采用近html上事件的命名规范,onxxx(onload, onclick等)。
当然,命名规范见仁见智,但我所论及的是js社区的普遍惯例。