2010-11-25 14:43:35
是时候将我发现的东西拿出来分享了 ---- 希望这对你有所帮助2010年感恩节 by jackie
变量命名中的[匈牙利命名法][骆驼命名法][帕斯卡(pascal)命名法]
[ 2011-01-10 09:40:30 | 作者: jackie ]
一、匈牙利命名法:广泛应用于象Microsoft Windows这样的环境中。
Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Microsoft 程序员查尔斯·西蒙尼(Charles Simonyi) 提出的。
匈牙利命名法通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。这些符号可以多个同时使用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。例如:m_lpszStr, 表示指向一个以0字符结尾的字符串的长指针成员变量。
匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。
匈牙利命名法中常用的小写字母的前缀:
前 缀 类 型
a 数组 (Array)
b 布尔值 (Boolean)
by 字节 (Byte)
c 有符号字符 (Char)
cb 无符号字符 (Char Byte,没有多少人用)
cr 颜色参考值 (ColorRef)
cx,cy 坐标差(长度 ShortInt)
dw Double Word
fn 函数
h Handle(句柄)
i 整型
l 长整型 (Long Int)
lp Long Pointer
m_ 类的成员
n 短整型 (Short Int)
np Near Pointer
p Pointer
s 字符串型
sz 以null做结尾的字符串型 (String with Zero End)
w Word
二、骆驼命名法:
骆驼式命令法,正如它的名称所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。例如,下面是分别用骆驼式命名法和下划线法命名的同一个函数:
骆驼式命名法近年来越来越流行了,在许多新的函数库和Microsoft
Windows这样的环境中,它使用得当相多。另一方面,下划线法是c出现后开始流行起来的,在许多旧的程序和UNIX这样的环境中,它的使用非常普遍。
三、帕斯卡(pascal)命名法:
与骆驼命名法类似。只不过骆驼命名法是首字母小写,而帕斯卡命名法是首字母大写
如:
二者都是采用了帕斯卡命名法.
在C#中,以帕斯卡命名法和骆驼命名法居多。
简单说
MyData 就是一個帕斯卡命名的示例
而myData是一個骆驼命名法,它第一個單詞的第一個字母小寫,後面的單詞首字母大寫,看起來像一個骆驼
而iMyData是一個匈牙利命名法,它的小寫的i說明了它的型態,後面的和帕斯卡命名相同,指示了該變量的用途.
Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Microsoft 程序员查尔斯·西蒙尼(Charles Simonyi) 提出的。
匈牙利命名法通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。这些符号可以多个同时使用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。例如:m_lpszStr, 表示指向一个以0字符结尾的字符串的长指针成员变量。
匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。
匈牙利命名法中常用的小写字母的前缀:
前 缀 类 型
a 数组 (Array)
b 布尔值 (Boolean)
by 字节 (Byte)
c 有符号字符 (Char)
cb 无符号字符 (Char Byte,没有多少人用)
cr 颜色参考值 (ColorRef)
cx,cy 坐标差(长度 ShortInt)
dw Double Word
fn 函数
h Handle(句柄)
i 整型
l 长整型 (Long Int)
lp Long Pointer
m_ 类的成员
n 短整型 (Short Int)
np Near Pointer
p Pointer
s 字符串型
sz 以null做结尾的字符串型 (String with Zero End)
w Word
二、骆驼命名法:
骆驼式命令法,正如它的名称所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。例如,下面是分别用骆驼式命名法和下划线法命名的同一个函数:
printEmployeePaychecks();
print_employee_paychecks();
第一个函数名使用了骆驼式命名法——函数名中的每一个逻辑断点都有一个大写字母来标记;第二个函数名使用了下划线法----函数名中的每一个逻辑断点都有一个下划线来标记。print_employee_paychecks();
骆驼式命名法近年来越来越流行了,在许多新的函数库和Microsoft
Windows这样的环境中,它使用得当相多。另一方面,下划线法是c出现后开始流行起来的,在许多旧的程序和UNIX这样的环境中,它的使用非常普遍。
三、帕斯卡(pascal)命名法:
与骆驼命名法类似。只不过骆驼命名法是首字母小写,而帕斯卡命名法是首字母大写
如:
public void DisplayInfo();
string UserName;
string UserName;
二者都是采用了帕斯卡命名法.
在C#中,以帕斯卡命名法和骆驼命名法居多。
简单说
MyData 就是一個帕斯卡命名的示例
而myData是一個骆驼命名法,它第一個單詞的第一個字母小寫,後面的單詞首字母大寫,看起來像一個骆驼
而iMyData是一個匈牙利命名法,它的小寫的i說明了它的型態,後面的和帕斯卡命名相同,指示了該變量的用途.
WIN7下关闭IPV6隧道适配器命令
[ 2010-12-08 14:57:10 | 作者: jackie ]
WINDOWS 会自发建立一条IPV6的隧道,通常我们用ipconfig /all就会看到很多条隧道,这是因为windows在IPv6迁移过程中需要使用一种或多种IPv6过渡技术。用以下3条命令把ipv6的接口关闭即可
若想还原则用以下命令:
netsh interface teredo set state disable
netsh interface 6to4 set state disabled
netsh interface isatap set state disabled
netsh interface 6to4 set state disabled
netsh interface isatap set state disabled
若想还原则用以下命令:
netsh interface teredo set state default
netsh interface 6to4 set state default
netsh interface isatap set state default
netsh interface 6to4 set state default
netsh interface isatap set state default
SQL取得下一个生日所剩天数
[ 2010-12-08 14:20:56 | 作者: jackie ]
因为工作上的需要,最近在开发时,需要查询所有7天内过生日的用户
本以为不是什么麻烦的事情,判断一下日期就了事的东西,却折腾了我大半天
最终,我决定在SQL中自己写了个方法用于获取下一个生日所剩天数
以下是具体代码:
IF OBJECT_ID (N'dbo.FN_GetNextBirthday', N'FN') IS NOT NULL
DROP FUNCTION dbo.FN_GetNextBirthday;
GO
CREATE FUNCTION dbo.FN_GetNextBirthday (@dt DATETIME)
CREATE FUNCTION dbo.FN_GetNextBirthday (@dt DATETIME, @today DATETIME)
RETURNS INT
AS
BEGIN
declare@today datetime,@days int;
select@today=GETDATE(), @dt=DATEADD(YEAR,YEAR(@today)-YEAR(@dt), @dt);
IF(@dt>@today)
set @days=DATEDIFF(DAY, @today, @dt); --//今年的生日
ELSE
set @days=DATEDIFF(DAY, @today, DATEADD(YEAR, 1, @dt)); --//明年的生日
RETURN(@days);
END
GO
调用方法:
select dbo.FN_GetNextBirthday('1988-12-12'); --//result 4
select dbo.FN_GetNextBirthday('1988-12-12',getdate()); --//result 4
放到WHERE条件中:
select username,birthday from member
where dbo.FN_GetNextBirthday(birthday)<=7
select username,birthday from member
where dbo.FN_GetNextBirthday(birthday, getdate())<=7
OK,大概就这些了,希望这对你有所帮助!
------------------------时间分隔线----------------------------------------
今天发现一个问题,2000的SQL函数内不能使用"getdate()",已改正。
本以为不是什么麻烦的事情,判断一下日期就了事的东西,却折腾了我大半天
最终,我决定在SQL中自己写了个方法用于获取下一个生日所剩天数
以下是具体代码:
IF OBJECT_ID (N'dbo.FN_GetNextBirthday', N'FN') IS NOT NULL
DROP FUNCTION dbo.FN_GetNextBirthday;
GO
CREATE FUNCTION dbo.FN_GetNextBirthday (@dt DATETIME, @today DATETIME)
RETURNS INT
AS
BEGIN
declare
select
IF(@dt>@today)
set @days=DATEDIFF(DAY, @today, @dt); --//今年的生日
ELSE
set @days=DATEDIFF(DAY, @today, DATEADD(YEAR, 1, @dt)); --//明年的生日
RETURN(@days);
END
GO
调用方法:
select dbo.FN_GetNextBirthday('1988-12-12',getdate()); --//result 4
放到WHERE条件中:
where dbo.FN_GetNextBirthday(birthday)<=7
select username,birthday from member
where dbo.FN_GetNextBirthday(birthday, getdate())<=7
OK,大概就这些了,希望这对你有所帮助!
------------------------时间分隔线----------------------------------------
今天发现一个问题,2000的SQL函数内不能使用"getdate()",已改正。
面向对象的JavaScript编程
[ 2010-12-08 08:24:46 | 作者: jackie ]
Javascript对于做过Web程序的人不应该是陌生,初期是用来做一些简单的FORM验证,基本上是在玩弄一些技巧性的东西。IE 4.0引入了DHTML,同时为了对抗Netscape的Javascript,提出了自己的脚本语言JScript,除了遵循EMAC的标准之外,同时增加了许多扩展,如下要提到的OOP编程就是其中的一个,为了命且概念,我以下提到的Javascript都是Microsoft Internet Explorer 4.0以上实现的JScript,对于Netscape,我没有做过太多的程序,所以一些的区别也就看出来。
Javascript不是一个支持面向对象的语言,更加算不上一个开发平台,但是Javascript提供了一个非常强大的基于prototype的面向对象调用功能,你可以在你自己需要的地方使用他们。因此,如何使用对象?本文尽可能从Javascript面向对象实现原理出发,解析清楚它的工作模型。在了解这些模型之后,你可以在自己的脚本库中编写一些实现代码,然后在其他地方调用。
Javascript的语法和C++很接近,不过在类实现中没有使用关键字Class,实现继承的时候也没有采用传统的Public或者Implement等等所谓的关键字来标示类的实现。这样的情况下,可能有就有人会问,如何编写Javascript的Class,如何实现继承。我开始也是百思不得其解,后来看了MSDN,才知道采用了prototype来实现,包括继承和重载,也可以通过这个关键字来实现。
Javascript的函数很奇怪,每个都是默认实现了Optional的,即参数都可以可选的,function a(var1,var2,var3),在调用的过程中a(),a(value1),a(value1,value2)等等的调用都是正确的,至少在即使编译部分可以完整通过,至于其它,只是和函数的实现逻辑比较相关了。
以下就JS对于类的实现、继承、重载详细介绍其实现方式。
1。实现
Js类的实现就通过函数直接实现的,每个函数可以直接看成class,如下代码
1)this."<Property or Method"的方式实现,在类声明函数中直接给出函数的实现,如 this.Add=new function(strUserName,strPassword)这样的方式调用,编写的方式在Class Function中调用。
2)通过ClassFunction.prototype.[FunctionName]=function(var1,var2...){//todo}这样的方式完成调用。
这两种方式从目标来看是一致的,按照我个人的观点来看,区别的只是在于实现方式,通过this.propertyName的方式来创建,Jscript自动创建了property或者method的入口,不过从程序的角度而言,还是使用prototype的关键字实现比较灵活。
另外Javascript也可以和我们C++中那种嵌套声明的方法来声明,C++实现的方法如下
实现了属性和方法,剩下的就是Event的实现了,我查找了许多资料,包括整个MSDN关于JScript的参考,都没有看到一个很好的模型关于事件实现的,后来参考了一些站点编写HTA(HTML Component,有空我会写一些相关的文章)的实现,借助于比较扭曲(我个人认为)的方法可以大致的实现基于事件驱动的功能。大致的思路是这样子的:
1).将所有的事件定义成属性,只要简单的声明就可以
2).在需要触发事件的代码中判断事件属性是否是一个函数,如果是函数,直接执行函数代码,如果是字符串,那么执行字符串函数,通过eval(str)来执行。
3) .在类的实例当中注册事件函数。
为了简单说明如上的思路,采用timer这样简单的例子来表述如上的所提到的内容,如果只是为了简单的实现timer的功能,Javascript中setInterval函数就可以满足全部的要求,如下的代码只是用来说明Timer的工作原理。
实际工作代码是在TimerCallBack()上面实现,事件触发作为属性的方式来实现,在应用实例中,代码提供了三种方法去调用事件,不过在事件的回调当中,我还没有想到如何可以带参数,只有才各自的实现当中访问各自需要的属性才能够实现全部的要求。
2。继承。
刚采用了大篇幅的文字去介绍如何实现Javascript的各种实现,也就是从逻辑上完成了一个封装class的实现,从某种意义上来说,class的实现是真正脚本编程中使用最多的部分,不过如果只是要完成如上的功能,使用VBScript来编写更能更加清晰,毕竟VBscript提供了class关键字,同时提供了public 和private这两个关键字,可以清晰的将公共和私有对象分离,至于事件的实现,也可以采用类似Javascript实现的思路,只是对于函数的引用需要采用GetRef这个函数,具体的用法可以参考scripting reference,MSDN里头也有详细的介绍,而Javascript强大至于在于如下要说的了,虽然具体的东西可能不多。
如上所言,我们已经完成了一个基本的类实现Timer,现在要做的是重新编写这个类,我们简单的只是想在这个类之中加入一个方法,提供当前的系统时间,方法的名称为getSystemDate,显然如果全部重新编写,那就失去了我这里说的意义了。先看看如下的实现。
3。重载
或许这个是OOP编程中比较复杂的地方了,在Javascript的实现中有点无奈,也就是通过prototype的方式来完成的,不过因为我不清楚如何调用父类的实现函数,那么在重载中只能够重新编写所有的实现了,另外就是在实现中实例化一个父类,然后通过调用它来返回需要的东西。
Javascript中所有的对象都是从Object继承下来的,object提供了toString()的方法,也就是说如果调用alert(objInstance)这样的过程,实际上是调用了alert(objInstance.toString())的方法,如果没有编写实现,object默认的toString()都是"object object"这样子的,在许多地方需要重载这个函数的,比如Timer,如果我们希望var ins=new Timer(5);alert(ins)调用得到的是interval的值5,那么就需要重新编写toString()方法了
原文:http://dev.csdn.net/htmls/19/19401.html
Javascript不是一个支持面向对象的语言,更加算不上一个开发平台,但是Javascript提供了一个非常强大的基于prototype的面向对象调用功能,你可以在你自己需要的地方使用他们。因此,如何使用对象?本文尽可能从Javascript面向对象实现原理出发,解析清楚它的工作模型。在了解这些模型之后,你可以在自己的脚本库中编写一些实现代码,然后在其他地方调用。
Javascript的语法和C++很接近,不过在类实现中没有使用关键字Class,实现继承的时候也没有采用传统的Public或者Implement等等所谓的关键字来标示类的实现。这样的情况下,可能有就有人会问,如何编写Javascript的Class,如何实现继承。我开始也是百思不得其解,后来看了MSDN,才知道采用了prototype来实现,包括继承和重载,也可以通过这个关键字来实现。
Javascript的函数很奇怪,每个都是默认实现了Optional的,即参数都可以可选的,function a(var1,var2,var3),在调用的过程中a(),a(value1),a(value1,value2)等等的调用都是正确的,至少在即使编译部分可以完整通过,至于其它,只是和函数的实现逻辑比较相关了。
以下就JS对于类的实现、继承、重载详细介绍其实现方式。
1。实现
Js类的实现就通过函数直接实现的,每个函数可以直接看成class,如下代码
function ClassTest1(){
...//implement code
}
var a=new ClassTest1
function ClassTest2(var1){
...//implement code
}
var b=new ClassTest("value")
对于类的属性,可以通过两种方式实现...//implement code
}
var a=new ClassTest1
function ClassTest2(var1){
...//implement code
}
var b=new ClassTest("value")
1)this."<Property or Method"的方式实现,在类声明函数中直接给出函数的实现,如 this.Add=new function(strUserName,strPassword)这样的方式调用,编写的方式在Class Function中调用。
2)通过ClassFunction.prototype.[FunctionName]=function(var1,var2...){//todo}这样的方式完成调用。
这两种方式从目标来看是一致的,按照我个人的观点来看,区别的只是在于实现方式,通过this.propertyName的方式来创建,Jscript自动创建了property或者method的入口,不过从程序的角度而言,还是使用prototype的关键字实现比较灵活。
另外Javascript也可以和我们C++中那种嵌套声明的方法来声明,C++实现的方法如下
Public Class ClassName:ParentClass{
Public DataType FunctionName(){
}
Public Class ClassName{
Public DataType FunctionName(){
}
}
}
在Javascript当中,当然不存在class这样的关键字了,所以实现起来有点戏剧性,不过仍然为一个非常巧妙的实现。Public DataType FunctionName(){
}
Public Class ClassName{
Public DataType FunctionName(){
}
}
}
function className(){
//Property Implement
this.UserName="blue";
//Method Implement
this.Add=new function(){
}
//Sub Class Implement
function SubClassName(){
this.PropertyName="hi"
}
//sub class method implement
SubClassName.prototype.Change=function{
}
}
//Main Class Method Implement
className.prototype.Delete=function(){
}
如上的代码大致演示了Javascript类中属性和方法的实现,另外有一点比较困惑,整个class中都是public的,没有关键字private之类的可以控制某些方法是否隐藏,那么在我们编写代码实现的规范中,我看国外一些程序员都是使用_functionName这样子为函数命的方法来区分,但是在调用过程中实际还可以调用的。//Property Implement
this.UserName="blue";
//Method Implement
this.Add=new function(){
}
//Sub Class Implement
function SubClassName(){
this.PropertyName="hi"
}
//sub class method implement
SubClassName.prototype.Change=function{
}
}
//Main Class Method Implement
className.prototype.Delete=function(){
}
实现了属性和方法,剩下的就是Event的实现了,我查找了许多资料,包括整个MSDN关于JScript的参考,都没有看到一个很好的模型关于事件实现的,后来参考了一些站点编写HTA(HTML Component,有空我会写一些相关的文章)的实现,借助于比较扭曲(我个人认为)的方法可以大致的实现基于事件驱动的功能。大致的思路是这样子的:
1).将所有的事件定义成属性,只要简单的声明就可以
2).在需要触发事件的代码中判断事件属性是否是一个函数,如果是函数,直接执行函数代码,如果是字符串,那么执行字符串函数,通过eval(str)来执行。
3) .在类的实例当中注册事件函数。
为了简单说明如上的思路,采用timer这样简单的例子来表述如上的所提到的内容,如果只是为了简单的实现timer的功能,Javascript中setInterval函数就可以满足全部的要求,如下的代码只是用来说明Timer的工作原理。
//Class For Timer
function Timer(iInterval){
//if not set the timer interval ,then defalut set to 500ms
this.Interval=iInterval || 500;
this._handleInterval;
this.TimerEvent=null
function Start(){
if(this.Interval!=0){
this._handleInterval=setInterval("TimerCallBack()",this.Interval);
}
}
function Start(){
clearInterval(this._handleInterval);
}
function TimerCallBack(){
if (typeof this.TimerEvent=="function"){
this.TimerEvent();
}
else if(this.TimerEvent!=null && this.TimerEvent.length>0){
eval(this.TimerEvent);
}
}
}
//Code for Instance
var t=new Timer(3);
//------------------------------------//
//1.
t.TimerEvent=function(){
//todo
}
//2.
t.TimerEvent="alert(\"hello\")";
//3.
t.TimerEvent=tTimerCall;
//----------------------------------//
t.Start();
t.Stop();
function tTimerCall(){
}
function Timer(iInterval){
//if not set the timer interval ,then defalut set to 500ms
this.Interval=iInterval || 500;
this._handleInterval;
this.TimerEvent=null
function Start(){
if(this.Interval!=0){
this._handleInterval=setInterval("TimerCallBack()",this.Interval);
}
}
function Start(){
clearInterval(this._handleInterval);
}
function TimerCallBack(){
if (typeof this.TimerEvent=="function"){
this.TimerEvent();
}
else if(this.TimerEvent!=null && this.TimerEvent.length>0){
eval(this.TimerEvent);
}
}
}
//Code for Instance
var t=new Timer(3);
//------------------------------------//
//1.
t.TimerEvent=function(){
//todo
}
//2.
t.TimerEvent="alert(\"hello\")";
//3.
t.TimerEvent=tTimerCall;
//----------------------------------//
t.Start();
t.Stop();
function tTimerCall(){
}
实际工作代码是在TimerCallBack()上面实现,事件触发作为属性的方式来实现,在应用实例中,代码提供了三种方法去调用事件,不过在事件的回调当中,我还没有想到如何可以带参数,只有才各自的实现当中访问各自需要的属性才能够实现全部的要求。
2。继承。
刚采用了大篇幅的文字去介绍如何实现Javascript的各种实现,也就是从逻辑上完成了一个封装class的实现,从某种意义上来说,class的实现是真正脚本编程中使用最多的部分,不过如果只是要完成如上的功能,使用VBScript来编写更能更加清晰,毕竟VBscript提供了class关键字,同时提供了public 和private这两个关键字,可以清晰的将公共和私有对象分离,至于事件的实现,也可以采用类似Javascript实现的思路,只是对于函数的引用需要采用GetRef这个函数,具体的用法可以参考scripting reference,MSDN里头也有详细的介绍,而Javascript强大至于在于如下要说的了,虽然具体的东西可能不多。
如上所言,我们已经完成了一个基本的类实现Timer,现在要做的是重新编写这个类,我们简单的只是想在这个类之中加入一个方法,提供当前的系统时间,方法的名称为getSystemDate,显然如果全部重新编写,那就失去了我这里说的意义了。先看看如下的实现。
function NewTimer(iInterval){
//call super
this.base=Timer;
this.base(iInterval);
}
NewTimer.prototype=new Timer;
NewTimer.prototype.getSystemDate=function(){
var dt=new Date();
return dt.getYear()+"-"+dt.getMonth()+"-"+dt.getDay();
}
上述代码实现了NewTimer类,从Timer继承,Javascript没有使用“:”或者java的public那样类似的关键字,只是通过newclassname.prototype=new baseclass这样的方法来完成,同时NewTimer实现了getSystemDate的方法,在NewTimer的初始化函数中,我使用了this.base=Timer,是为了引用父类的实现,不过在对于父类其他实现函数的调用,到现在我没有找到一个确定的方法,是否通过this.base.start()那样来调用还是其他的,如果有谁比较清楚的,麻烦告诉我,另外在netscape的站点上,我查到有一个特殊的"__proto__"的属性好像是对于父类的直接引用,不过具体的我也没有尝试过,在msdn中也没有看到对于__proto__的支持。//call super
this.base=Timer;
this.base(iInterval);
}
NewTimer.prototype=new Timer;
NewTimer.prototype.getSystemDate=function(){
var dt=new Date();
return dt.getYear()+"-"+dt.getMonth()+"-"+dt.getDay();
}
3。重载
或许这个是OOP编程中比较复杂的地方了,在Javascript的实现中有点无奈,也就是通过prototype的方式来完成的,不过因为我不清楚如何调用父类的实现函数,那么在重载中只能够重新编写所有的实现了,另外就是在实现中实例化一个父类,然后通过调用它来返回需要的东西。
Javascript中所有的对象都是从Object继承下来的,object提供了toString()的方法,也就是说如果调用alert(objInstance)这样的过程,实际上是调用了alert(objInstance.toString())的方法,如果没有编写实现,object默认的toString()都是"object object"这样子的,在许多地方需要重载这个函数的,比如Timer,如果我们希望var ins=new Timer(5);alert(ins)调用得到的是interval的值5,那么就需要重新编写toString()方法了
Timer.prototype.toString=function(){ return this.Interval};
以上代码实现之后alert(ins)得到的就是5了。原文:http://dev.csdn.net/htmls/19/19401.html
学习Javascript闭包(Closure)
[ 2010-12-08 08:11:15 | 作者: jackie ]
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
下面就是我的学习笔记,对于Javascript初学者应该是很有用的。
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
上一节代码中的f2函数,就是闭包。
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
怎么来理解这句话呢?请看下面的代码。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
六、思考题
如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。
代码片段一。
(完)
原文网址:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
下面就是我的学习笔记,对于Javascript初学者应该是很有用的。
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
另一方面,在函数外部自然无法读取函数内的局部变量。function f1(){
alert(n);
}
f1(); // 999
function f1(){
var n=999;
}
alert(n); // error
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!var n=999;
}
alert(n); // error
function f1(){
n=999;
}
f1();
alert(n); // 999
二、如何从外部读取局部变量?n=999;
}
f1();
alert(n); // 999
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1(){
n=999;
function f2(){
alert(n); // 999
}
}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。n=999;
function f2(){
alert(n); // 999
}
}
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1()
result(); // 999
三、闭包的概念n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1()
result(); // 999
上一节代码中的f2函数,就是闭包。
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
怎么来理解这句话呢?请看下面的代码。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
六、思考题
如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。
代码片段一。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
代码片段二。var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
(完)
原文网址:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
javascript性能优化
[ 2010-12-07 10:28:03 | 作者: jackie ]
很久就想总结一下关于javascript性能优化方面的一些东西,平时也有注意收集这方面的资料。把del.icio.us里的收藏的东西翻出来看一遍,才惊奇地发现,这些所谓的优化方法大多出自《javascript高级程序设计》一书,当然也有个别
不一样的。总之这本书上关于javascript性能优化的内容足足用了近20页来进行阐述,所以今天我也照本宣科地来介绍一下,同时收录其他方法。
javascript的性能优化应分为两部分:下载时间和执行时间。
下载时间
javascript作为一种解释性语言不同于其他编程语言。在诸如java,c,c++的语言中,开发人员根本无需考虑变量名的长度以及长篇大论的注释,因为这些在编译时都会被删除。
但是web浏览器下载的是javascript的源代码,你编写的javascript程序文件会原模原样地下载到客户端。而这些长变量名和注释就会影响脚本的下载时间。单个TCP-IP包中能放入的字节数是1160,所以最好将每个javascript文件的大小控制在1160字节以内以获取最优的下载时间。
javascript文件中的任何字节,不管是空格,还是换行都会影响javascript文件的下载时间,所以在部署任何javascript文件之前,都应该尽可能地优化脚本文件的体积。以下是一些常用的优化javascript文件大小的方法:
1、删除注释
脚本中的任何注释都应该在部署之前被删除。虽然注释对于开发人员来说意义重大,但在部署时这些注释对客户端用户是没有任何实质意义的,而更可怕的是很多javascript源代码中的注释会比代码多得多。因此删除注释是所见javascript文件大小最为方便有效的途径。
2、删除制表符和空格
又规律地缩进代码有增强代码的可阅读性,但是浏览器/客户端用户并不需要这些额外的制表符和可个,所以最好删除它们,包括函数参数,赋值语句和比较操作符之间的空格:
和前一条规则一样,只要你在程序的每行结尾都正确地添加了分号,那么就不需要再添加换行符了。
4、替换变量名
此方法很无聊,大致做法就是将所有变量名替换成无意义的简短变量名。
要手动地完成以上四步,在实际中可能会有一定的难度,那么下面的一些链接可能对你有所帮助。
ECMAScript Cruncher:http://saltstorm.net/depo/esc/
JSMin(The JavaScript Minifier): http://www.crockford.com/javascript/jsmin.html
Online JavaScript Compressor:http://dean.edwards.name/packer/
5、替换布尔值
对于比较来说,true等译1,false等于0。因此可以将字面量的true都用1来替换,而false用0来替换,进而较少一定的字节数。
6、缩短否定检测
代码中常常会出现检测某个值时候有效的语句。而大部分否定测试所做的就是判断某个变量是否为undefined、null或者false,如下:
执行时间
javascript是一种解释性语言,它的执行速度要大大慢于编译型语言。据测试,javascript的执行效率要比编译型的C程序慢5000倍;比解释型的Java慢100倍;比解释型的Perl慢10倍。但是我们仍然可以通过一些简单的事情来提高javascript的效率,同时这也显得更加重要。
1、使用局部变量
在函数中,总是使用var来定义变量。无论何时使用var都会在当前的范围类创建一个局部变量。如果不使用var来定义变量,那么变量会被创建在window范围内,那么每次使用这个变量的时候,解释程序都会搜索整个范围树。同时全局变量要在页面从浏览器中卸载后才销毁,而局部变量在函数执行完毕即可销毁,过多的全局变量增加了不必要的内存消耗。
2、避免使用with语句
使用with语句能够减少一定的代码长度,但是在使用with语句时,要强制解释程序不仅在范围树内查找局部变量,还强制检测每个变量及指定的对象,看其是否为特性。因为,我们也可以在函数中定义同明的变量。
3、选择正确的算法
只要有可能就应该用局部变量或者数字索引的数组来替代命名特性。如果命名特性要多次使用,就先将它的值存储在局部变量中,避免多次使用线性算法请求命名特性的值。
循环在各种编程语言中得到大量应用,所以保持循环的高效性尤为重要。按照反向的顺序进行循环迭代是一种有效的方法。
5、翻转循环
用do..while循环来替代while循环以进一步减少执行时间。假设有如下while循环:
这段代码比用while循环更快,因为它用循环反转来进一步地优化:
6、展开循环
可以考虑将循环展开,一次执行多个语句。考虑如下for循环例子:
循环总共要执行20次,每次都是对变量sum进行增量操作,但是可以这样写:
在循环体内做了五次增量。每次增量后,都对变量i加1,所以多数组的遍历与原来是一致的,但是控制语句只执行了四次,减少了执行时间。当然还可以继续优化:
7、优化if语句
使用if语句和多个else语句时,一定要把最有可能的情况放在第一个,然后是第二可能出现的情况,如此排列,这样就减少了要进行多次测试才能遇到正确条件的情况。
同时也要尽量减少else if语句的数量,并且将条件按照二叉树的方式进行排列。例如:
8、switch和if
一般来说超过两种情况时,最好使用switch语句。常用switch来代替if语句,最高可令执行快10倍。在javascript中就更加可以从中获益,因为case语句可以使用任何类型的值。
9、避免字符串连接
一旦一次要使用多个字符串的连接(比如,大于五个),最好使用如下方式:
只要可能,就应该考虑优先使用内置方法。因为内置方法是用C++或者C之类的语言编译的,运行起来比必须实时编译的javascript要高效的多。比如你可能像要自己编写一个求阶乘的函数,但是实际上你应该使用javascript内置的Math.pow()方法。
11、存储常用的值
当多次使用到一个值得时候,可先将其存储在局部变量中以便快速访问。尤其对于通常使用对象的特性来进行访问的值更加重要。如:
document.body.clientWidth在该例中被使用了两次,但它是使用命名特性(属于极其昂贵的操作)来获取的。可以使用局部变量来重写这段代码:
12、最小化语句数量
我们有理由相信,脚本中语句越少,执行所需要的时间越短。很多方法可以将javascript中的语句数量减到最少,比如定义变量时,处理迭代数字时,使用数组和对象字面量时。
多个变量的定义可用一个var语句来替代:
插入迭代子
使用迭代子(在不同位置上加减的值)时,尽可能合并语句。考虑下面代码:
13、节约使用DOM
不管是添加、删除或者是其他对页面DOM内部结构的更改,都会导致整个页面的重新渲染,带来的是明显的时间消耗。解决这个问题的方法是尽可能地对不在DOM文档中的元素节点进行操作。如下例子:
每次执行I都会对整个页面重新渲染一次,执行II又会对整个页面重新渲染一次,总共会有20次的对页面的渲染。我们大可使用文档碎片来保存所有列表项,最后再一起添加到文档中。
其他一些优化方法
1、不要使用eval
使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间。这时候使用JavaScript所支持的闭包可以实现函数模版(关于闭包的内容请参考函数式编程的有关内容)
2、类型转换
类型转换是大家常犯的错误,因为JavaScript是动态类型语言,你不能指定变量的类型。
对字符串进行循环操作,譬如替换、查找,应使用正则表达式,因为本身JavaScript的循环速度就比较慢,而正则表达式的操作是用C写成的语言的API,性能很好。
4、DOM相关
不一样的。总之这本书上关于javascript性能优化的内容足足用了近20页来进行阐述,所以今天我也照本宣科地来介绍一下,同时收录其他方法。
javascript的性能优化应分为两部分:下载时间和执行时间。
下载时间
javascript作为一种解释性语言不同于其他编程语言。在诸如java,c,c++的语言中,开发人员根本无需考虑变量名的长度以及长篇大论的注释,因为这些在编译时都会被删除。
但是web浏览器下载的是javascript的源代码,你编写的javascript程序文件会原模原样地下载到客户端。而这些长变量名和注释就会影响脚本的下载时间。单个TCP-IP包中能放入的字节数是1160,所以最好将每个javascript文件的大小控制在1160字节以内以获取最优的下载时间。
javascript文件中的任何字节,不管是空格,还是换行都会影响javascript文件的下载时间,所以在部署任何javascript文件之前,都应该尽可能地优化脚本文件的体积。以下是一些常用的优化javascript文件大小的方法:
1、删除注释
脚本中的任何注释都应该在部署之前被删除。虽然注释对于开发人员来说意义重大,但在部署时这些注释对客户端用户是没有任何实质意义的,而更可怕的是很多javascript源代码中的注释会比代码多得多。因此删除注释是所见javascript文件大小最为方便有效的途径。
2、删除制表符和空格
又规律地缩进代码有增强代码的可阅读性,但是浏览器/客户端用户并不需要这些额外的制表符和可个,所以最好删除它们,包括函数参数,赋值语句和比较操作符之间的空格:
function doSomething ( arg1, arg2, arg3 ){
alert(arg1 + arg2 + arg3);
}
function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}
3、删除所有换行alert(arg1 + arg2 + arg3);
}
function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}
和前一条规则一样,只要你在程序的每行结尾都正确地添加了分号,那么就不需要再添加换行符了。
4、替换变量名
此方法很无聊,大致做法就是将所有变量名替换成无意义的简短变量名。
要手动地完成以上四步,在实际中可能会有一定的难度,那么下面的一些链接可能对你有所帮助。
ECMAScript Cruncher:http://saltstorm.net/depo/esc/
JSMin(The JavaScript Minifier): http://www.crockford.com/javascript/jsmin.html
Online JavaScript Compressor:http://dean.edwards.name/packer/
5、替换布尔值
对于比较来说,true等译1,false等于0。因此可以将字面量的true都用1来替换,而false用0来替换,进而较少一定的字节数。
6、缩短否定检测
代码中常常会出现检测某个值时候有效的语句。而大部分否定测试所做的就是判断某个变量是否为undefined、null或者false,如下:
if(oTest != undefined){
//dosomething
}
if(oTest != null){
//dosomething
}
if(oTest != false){
//dosomething
}
//这样写更简洁
if(!oTest){
//dosomethin
}
7、使用数组和对象变量//dosomething
}
if(oTest != null){
//dosomething
}
if(oTest != false){
//dosomething
}
//这样写更简洁
if(!oTest){
//dosomethin
}
var aTest = new Array;
var oTest = new Object;
oTest.pro1 = "pro1"
oTest.pro2 = "pro2";
和下面的代码作用是完全相同的,但是下面的代码可以节省更多的字节。var oTest = new Object;
oTest.pro1 = "pro1"
oTest.pro2 = "pro2";
var aTest = [];
var oTest = {pro1:"pro1",pro2:"pro2"};
var oTest = {pro1:"pro1",pro2:"pro2"};
执行时间
javascript是一种解释性语言,它的执行速度要大大慢于编译型语言。据测试,javascript的执行效率要比编译型的C程序慢5000倍;比解释型的Java慢100倍;比解释型的Perl慢10倍。但是我们仍然可以通过一些简单的事情来提高javascript的效率,同时这也显得更加重要。
1、使用局部变量
在函数中,总是使用var来定义变量。无论何时使用var都会在当前的范围类创建一个局部变量。如果不使用var来定义变量,那么变量会被创建在window范围内,那么每次使用这个变量的时候,解释程序都会搜索整个范围树。同时全局变量要在页面从浏览器中卸载后才销毁,而局部变量在函数执行完毕即可销毁,过多的全局变量增加了不必要的内存消耗。
2、避免使用with语句
使用with语句能够减少一定的代码长度,但是在使用with语句时,要强制解释程序不仅在范围树内查找局部变量,还强制检测每个变量及指定的对象,看其是否为特性。因为,我们也可以在函数中定义同明的变量。
3、选择正确的算法
只要有可能就应该用局部变量或者数字索引的数组来替代命名特性。如果命名特性要多次使用,就先将它的值存储在局部变量中,避免多次使用线性算法请求命名特性的值。
var aValues = [1,2,3,4,5,6,7];
function testFunc(){
for(var i=0, iCount=aValues.length; i++){
alert(i + "/" + iCount + "=" + aValues[i]);
}
}
4、反转循环function testFunc(){
for(var i=0, iCount=aValues.length; i++){
alert(i + "/" + iCount + "=" + aValues[i]);
}
}
循环在各种编程语言中得到大量应用,所以保持循环的高效性尤为重要。按照反向的顺序进行循环迭代是一种有效的方法。
for(var i=aValues.length-1; i >= 0; i--){
//do something
}
反转循环有利于减低算法的复杂度。它用常数0作为循环控制语句以减小执行时间。//do something
}
5、翻转循环
用do..while循环来替代while循环以进一步减少执行时间。假设有如下while循环:
var i=0;
while(i < aValues.length){
//do something
i++;
}
可用do..while循环重写上面的代码而不改变行为:while(i < aValues.length){
//do something
i++;
}
var i=0;
do{
//do something
i++;
}while(i < aValues.length)
do{
//do something
i++;
}while(i < aValues.length)
这段代码比用while循环更快,因为它用循环反转来进一步地优化:
var i=aValues.length-1;
do{
//do something
i--;
}while(i>=0)
也可以将自减操作直接放进控制语句中,以减少额外的语句。do{
//do something
i--;
}while(i>=0)
var i=aValues.length-1;
do{
//do somethin
}while(--i>=0)
这个循环已经被完全优化了do{
//do somethin
}while(--i>=0)
6、展开循环
可以考虑将循环展开,一次执行多个语句。考虑如下for循环例子:
var sum = 0;var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
}
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
}
循环总共要执行20次,每次都是对变量sum进行增量操作,但是可以这样写:
var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
}
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
}
在循环体内做了五次增量。每次增量后,都对变量i加1,所以多数组的遍历与原来是一致的,但是控制语句只执行了四次,减少了执行时间。当然还可以继续优化:
var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
}
关于此算法的更多信息可以参考这里http://home.earthlink.net/~kendrasg/info/js_opt/var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
}
7、优化if语句
使用if语句和多个else语句时,一定要把最有可能的情况放在第一个,然后是第二可能出现的情况,如此排列,这样就减少了要进行多次测试才能遇到正确条件的情况。
同时也要尽量减少else if语句的数量,并且将条件按照二叉树的方式进行排列。例如:
if(iNum>0){
if(iNum>10){
alert("Between 0 and 10");
}else{
if(iNum>20){
alert("Between 10 and 20");
}else{
if(iNum<30){
alert("Between 20 and 30");
}else{
alert("Greater than or equal to 30");
}
}
}
}else{
alert("Less than or equal to 0");
}
if(iNum>10){
alert("Between 0 and 10");
}else{
if(iNum>20){
alert("Between 10 and 20");
}else{
if(iNum<30){
alert("Between 20 and 30");
}else{
alert("Greater than or equal to 30");
}
}
}
}else{
alert("Less than or equal to 0");
}
8、switch和if
一般来说超过两种情况时,最好使用switch语句。常用switch来代替if语句,最高可令执行快10倍。在javascript中就更加可以从中获益,因为case语句可以使用任何类型的值。
9、避免字符串连接
一旦一次要使用多个字符串的连接(比如,大于五个),最好使用如下方式:
var buf = new Array();
for(var i = 0; i < 100; i++){
buf.push(i.toString());
}
var all = buf.join("");
10、优先使用内置方法for(var i = 0; i < 100; i++){
buf.push(i.toString());
}
var all = buf.join("");
只要可能,就应该考虑优先使用内置方法。因为内置方法是用C++或者C之类的语言编译的,运行起来比必须实时编译的javascript要高效的多。比如你可能像要自己编写一个求阶乘的函数,但是实际上你应该使用javascript内置的Math.pow()方法。
11、存储常用的值
当多次使用到一个值得时候,可先将其存储在局部变量中以便快速访问。尤其对于通常使用对象的特性来进行访问的值更加重要。如:
oDiv1.style.left = document.body.clientWidth;
oDiv2.style.left = document.body.clientWidth;
oDiv2.style.left = document.body.clientWidth;
document.body.clientWidth在该例中被使用了两次,但它是使用命名特性(属于极其昂贵的操作)来获取的。可以使用局部变量来重写这段代码:
var iClientWidth = document.body.clientWidth;
oDiv1.style.left = iClientWidth;
oDiv2.style.left = iClientWidth;
oDiv1.style.left = iClientWidth;
oDiv2.style.left = iClientWidth;
12、最小化语句数量
我们有理由相信,脚本中语句越少,执行所需要的时间越短。很多方法可以将javascript中的语句数量减到最少,比如定义变量时,处理迭代数字时,使用数组和对象字面量时。
多个变量的定义可用一个var语句来替代:
var iFive = 5, sColor = "red", aValues = [1,2,3], oDate = new Date();
插入迭代子
使用迭代子(在不同位置上加减的值)时,尽可能合并语句。考虑下面代码:
var sName = aValues[i];
i++;
这样写更简短:i++;
var sName = aValues[i++];
13、节约使用DOM
不管是添加、删除或者是其他对页面DOM内部结构的更改,都会导致整个页面的重新渲染,带来的是明显的时间消耗。解决这个问题的方法是尽可能地对不在DOM文档中的元素节点进行操作。如下例子:
var oUL = document.getElementById("ulItems");
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oUL.appendChild(oLI); //I
oLI.appendChild(document.createTextNode("Item "+i)); //II
}
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oUL.appendChild(oLI); //I
oLI.appendChild(document.createTextNode("Item "+i)); //II
}
每次执行I都会对整个页面重新渲染一次,执行II又会对整个页面重新渲染一次,总共会有20次的对页面的渲染。我们大可使用文档碎片来保存所有列表项,最后再一起添加到文档中。
var oUL = document.getElementById("ulItems");
var oFragment = document.createDocumentFragment();
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oLI.appendChild(document.createTextNode("Item "+i));
oFragment.appendChild(oLI);
}
oUL.appendChild(oFragment);
这样对文档中DOM树的操作就只有一次。var oFragment = document.createDocumentFragment();
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oLI.appendChild(document.createTextNode("Item "+i));
oFragment.appendChild(oLI);
}
oUL.appendChild(oFragment);
其他一些优化方法
1、不要使用eval
使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间。这时候使用JavaScript所支持的闭包可以实现函数模版(关于闭包的内容请参考函数式编程的有关内容)
2、类型转换
类型转换是大家常犯的错误,因为JavaScript是动态类型语言,你不能指定变量的类型。
- 把数字转换成字符串,应用”" + 1,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:(“” + ) > String() > .toString() > new String()这条其实和下面的“直接量”有点类似,尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。String()属于内部函数,所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。
- 浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()。另外,和第二节的对象查找中的问题不一样,Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。
- 对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高
对字符串进行循环操作,譬如替换、查找,应使用正则表达式,因为本身JavaScript的循环速度就比较慢,而正则表达式的操作是用C写成的语言的API,性能很好。
4、DOM相关
- 插入HTML 很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。
- 创建节点 尽量避免只是使用html字符串来创建节点。因为这样做既无法保证代码的有效性,同时字符串的操作效率有极低。所以应该是用document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素,应该先准备一个样板节点。
- 定时器 如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval。setTimeout每次要重新设置一个定时器








