More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  StephenChen's Tech SpacePhotosProfileFriendsMore Tools Explore the Spaces community

StephenChen's Tech Space

make difference
Updated 8/15/2008
Updated 5/17/2008
Updated 11/27/2007
Updated 11/27/2007

VMware6安装Ubuntu8解决分辨率和鼠标滚轮问题

 

用VMware6安装Ub8的时候默认的分辨率是800*600的,基本上怎么改都改不了。实际上要改变Ub8的分辨率,需要安装一个工具,即是VMware-tool,这个工具一般VMware都会在左下角提示安装的,安装这个工具后就可以随意地选择适合自己的分辨率。

安装过程大致如下:

1.VMware菜单栏VM,选择Install VMware-tool,要注意在虚拟机系统已经开启的前提下才能进行这个操作;
2.一般会虚拟机系统会弹出一个文件夹,里面有两个文件,分别是rpm和tar.bz格式,文件名都是类似VMwareTools-6.0.4-93057;
3.在终端用sudo apt-get install alien下载将rpm转换成deb包的工具;
4.alien VMwareTools-6.0.4-93057.rpm的方式将rpm转换成deb,会在当前的工具下生成一个deb包,且文件名相同;
5.点击安装转换后的deb包,然后一切默认;
6.安装后之后就运行脚本 /usr/bin/vmware-config-tools.pl,按照操作提示安装下去,一般都是默认的了,最后会提示选择的分辨率;
7.然后想改变分辨率的时候,同时可以运行上面的脚本文件。重新设置好分辨率后就直接重启,在登录界面会看到改变后的分辨率界面。

 

至于滚轮问题,用root的权限打开/etc/X11/xorg.conf文件,相应地将下面的内容加上去,将原来是"Configured Mouse"的配置内容注释掉#或者是删除,然后重启即可。虽然基本上解决了滚轮的问题,但是通过滚轮切换桌面还是不行。

Section "InputDevice"
    Identifier     "Configured Mouse"
    Driver         "vmmouse"
    Option         "CorePointer"
    Option         "Device"     "/dev/input/mice"
    Option         "Protocol"     "ImPS/2"
    Option         "Buttons"     "5"
    Option         "ZAxisMapping"     "4 5"
EndSection

[转]Session详解

摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。

目录:
一、术语session
二、HTTP协议与状态保持
三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用程序的session共享
八、总结
参考文档

一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义,“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者“一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html

这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分

浏览器在再次访问goolge的资源时自动向外发送cookie

使用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。

IE也可以设置在接受cookie前询问

这是一个询问接受cookie的对话框。

四、理解session机制
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域,cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用,Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)

1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有HttpSessionBindingListener,HttpSessionActivationListener和HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享
常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>

需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。

在Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下

对于这样一种结构,在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session);

应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");

值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。

javascript基础知识拾遗--表单

 

一、文本框

  HTML中有两种不同类型的文本框,单行的(<input type=”text”/>)和多行的(<textarea></textarea>)。单行的文本框可以通过size属性来控制输入字符的数量,多行的<textarea>可以通过cols和rows指定文本框的宽和高,但初始值必须在<textarea>和</textarea>之间,并不能指定允许的最大字符数。两种文本框都可以使用length属性来获取当前文本框中文本长度。

  选择文本可以通过focus()和select()两个方法并用使文本框获取焦点。除了blur和focus事件后,文本框还支持change事件和select事件。其中change事件是当用户更改内容后文本框失去焦点时发生(如果是通过value特性来更改内容则不会触发)。select事件是当一个或多个字符被选中时发生,无论是手工选中还是用select()方法。

  注意change事件和blur事件的区别。只要文本框失去焦点,就触发blur事件,而change事件只有当文本框内的文本发生改变后,失去焦点时才触发。如果文本不变,但文本框失去焦点,那么只有blur事件被触发;如果文本发生了变化,则先触发change事件,然后触发blur事件。

 

二、列表框和组合框

  列表框和组合框是通过HTML的<select/>元素来创建的。

访问选项:

DOM方法:

oListBox.options[1].firstChild.nodeValue; //显示text

oListBox.options[1].getAttribute(“value”); //显示value

HTML DOM方法:

oListBox.options[1].text;

oListBox.options[1].value;

每个option有一个index属性,表示它在options集合中的索引:

oListBox.options[1].index; //输入1

由于options是一个集合,因此也可以通过length属性来获取options的数量。

 

更新选项:

  <select/>有一个selectedIndex特性表示当前选中选项的索引(如果没有选中则返回-1)。列表框可以选择多个选项,,只要将<select/>元素的multiple特性设置为”multiple”,这时selectedIndex返回的是第一个被选中的索引。

  添加选项可以通过一系列的create*方法和appendChild方法来实现,而删除选项就可以直接这样oListBox.optioins[1]=null,或者oListBox.remove(1)。

C/C++面试题拾遗

1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)
int a = 4;
(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);
a = ?
答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;
改后答案依次为9,10,10,11

总结:(a++)不能作为左值进行赋值,但(++a)可以

2.某32位系统下, C++程序,请计算sizeof 的值(5分).
char str[] = “http://www.ibegroup.com/”
char *p = str ;
int n = 10;
请计算
sizeof (str ) = ?(1)
sizeof ( p ) = ?(2)
sizeof ( n ) = ?(3)
void Foo ( char str[100]){
请计算
sizeof( str ) = ?(4)
}
void *p = malloc( 100 );
请计算
sizeof ( p ) = ?(5)
答:(1)25 (2)4 (3) 4 (4)4 (5)4

总结:sizeof(数组变量名)得到正确的数组大小(包含后面的'\0',strlen不包含最后的'\0'),指针的大小为4字节,函数参数传递的是数组指针

3. void Test(void){
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL){
strcpy(str, “world”);
printf(str);
}
}
请问运行Test 函数会有什么样的结果?
答:输出“world”

总结:free()后就不能再引用free的指针, 但是并不把指针所指的内存里的数据删除,只是告诉操作系统这块内存,已经不用了,系统可以用于其他的地方,所以free后这块内存中的数据是不可预测的, 而且free操作并不把指针设置为NULL.所以在使用free()时在后面加上设置为NULL的语句,以免对野指针的引用,是程序有不可预料的结果.进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。

4.头文件的作用
(1)通过头文件来调用库的功能;
(2)头文件能加强类型安全检查。

5.const与#define的比较
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

6.辨析
String temp(s1+s2);
return temp;  (1)

return (s1+s2); (2)
的区别,s1和s2都是String
(1)首先,temp对象被创建,同时完成初始化,然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中,最后temp在函数结束时被销毁(调用析构函数)。
(2)这是创建一个临时对象并返回它,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的花费,提高了效率。

7.引用与指针的区别
(1)引用被创建的同时必须被初始化,指针可以在任何时间被初始化;
(2)引用不存在NULL引用,引用必须与合法的存储单元关联,而指针可以是NULL值;
(3)一旦引用被初始化,就不能改变引用的关系,而指针可以随时改变所指的对象。

8. 编写strcat函数(6分)
已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
(1)不调用C++/C 的字符串库函数,请编写函数 strcat
答:
VC源码:
char * __cdecl strcat (char * dst, const char * src)
{
char * cp = dst;
while( *cp )
cp++; /* find end of dst */
while( *cp++ = *src++ ) ; /* Copy src to end of dst */
return( dst ); /* return dst */
}
(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值?
答:方便赋值给其他变量

9.static有什么用途?(请至少说明两种)
(1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
(2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
(3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝。
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

10.写出float x 与“零值”比较的if语句。
      if(x>0.000001&&x<-0.000001)

11.数组指针
main()
{
  int a[5]={1,2,3,4,5};
   int *ptr=(int *)(&a+1);

   printf("%d,%d",*(a+1),*(ptr-1));
}
输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,
不同类型的指针+1之后增加的大小不同
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].

12.操作字符串函数的源码
(1)strcpy
char* strcpy(char* dest, const char* src)
{
    assert((dest!=NULL) && (src!=NULL));
    char* addr = src;
    while(*dest++ = *src++);
    return addr;
}
(2)strlen
int strlen(const char* str)
{
    assert(str != NULL);
    int len = 0;
    while(*str)
        len++;
    return len;
}

13.找出程序错误
swap(int *p1, int *p2)
{
    int *p;
    *p = *p1;
    *p1 = *p2;
    *p2 = *p;
}
错误:在swap函数中,p是一个"野"指针,有可能指向系统区,导致程序运行的崩溃。因此,程序应该更正如下:
swap(int *p1, int *p2)
{
    int p;
    p = *p1;
    *p1 = *p2;
    *p2 = p;
}

14.判断宏输出
#define MIN(A, B) ((A) > (B) ? (B) : (A))
int main()
{
    int a[4] = {10, 20, 30, 40};
    int *p = a;
    int b = 100;
    int least;
    least = MIN(*p++, b);
    printf("MIN : %d\n", least);
    printf("A : %d\n", *p);
    return 0;
}
输出的结果是:
MIN : 20
A : 30
总结:MIN(*p++, b) 相当于 ((*p++) : b ? (*p++) : b),指针p产生了两次自增操作

15.编写一个函数,作用是把一个char组成的字符串循环
右移n个。比如原来是“abcdefghi”如果n=2,移位后应该
是“hiabcdefgh”
void LoopMove(char* str, int step)
{
    int len = strlen(str);
    int unmove = len - step;
    int size = len + step + 1;
    char *temp = (char*)malloc(size);
    strcpy(temp, str+unmove);
    strcpy(temp+step, str);
    temp[len] = '\0';
    strcpy(str, temp);
    free(temp);
}

16.memcpy
//void * memcpy ( void * destination, const void * source, size_t num );
#include <stdio.h>
#include <string.h>

int main ()
{
  char str1[]="Sample string";
  char str2[40];
  char str3[40];
  memcpy (str2,str1,strlen(str1)+1);
  memcpy (str3,"copy successful",16);
  printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
  return 0;
}
output:
str1: Sample string
str2: Sample string
str3: copy successful

17.memset
//void * memset ( void * ptr, int value, size_t num );
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "almost every programmer should know memset!";
  memset (str,'-',6);
  puts (str);
  return 0;
}
Output:
------ every programmer should know memset!

18.编写类String的构造函数、析构函数和赋值函数
String::String(const char *str)
{
   if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断
     {
       m_data = new char[1] ;
       m_data[0] = '\0' ;
     }
   else
    {
       m_data = new char[strlen(str) + 1];
       strcpy(m_data,str);
    }
}
String::String(const String &another)
{
    m_data = new char[strlen(another.m_data) + 1];
    strcpy(m_data,other.m_data);
}

String& String::operator =(const String &rhs)
{
    if ( this == &rhs)
        return *this ;
    delete []m_data; //删除原来的数据,新开一块内存
    m_data = new char[strlen(rhs.m_data) + 1];
    strcpy(m_data,rhs.m_data);
    return *this ;
}

String::~String()
{
    delete []m_data ;
}

19.const关键字的作用
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二都是同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const函数,则表明其为一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,若指定其返回值为const类型,则其返回值不能作为"左值"。

20.C++中的空类,默认产生哪些类成员函数
class Empty
{
    public:
        Empty(); //缺省构造函数
        Empty(const Empty&); //拷贝构造函数
        ~Empty(); //析构函数
        Empty& operator=(const Empty&); //赋值运算符
        Empty* operator&(); //取址运算符
        const Empty* operator&() const; //取址运算符const
}

21.一个由C/C++编译的程序占用的内存组成结构:
(1)栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量
的值等。其操作方式类似于数据结构中的栈。
(2)堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
(3)全局区(静态区)(static) :全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
(4)文字常量区 :常量字符串就是放在这里的,程序结束后由系统释放。
(5)程序代码区:存放函数体的二进制代码。
事例代码:
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //"123456"在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456");  //"123456"放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}

[转]Javascript的IE和Firefox兼容性汇编

以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox
1. document.form.item 问题
(1)现有问题:
  现有代码中存在许多 document.formName.item("itemName") 这样的语句,不能在 MF 下运行
(2)解决方法:
  改用 document.formName.elements["elementName"]
(3)其它
  参见 2


2. 集合类对象问题
(1)现有问题:
  现有代码中许多集合类对象取用时使用 (),IE 能接受,MF 不能。
(2)解决方法:
  改用 [] 作为下标运算。如:document.forms("formName") 改为 document.forms["formName"]。
  又如:document.getElementsByName("inputName")(1) 改为 document.getElementsByName("inputName")[1]
(3)其它


3. window.event
(1)现有问题:
  使用 window.event 无法在 MF 上运行
(2)解决方法:
MF 的 event 只能在事件发生的现场使用,此问题暂无法解决。可以这样变通:
  原代码(可在IE中运行):
<input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit()"/>
...
<script language="javascript">
function gotoSubmit() {
...
alert(window.event); // use window.event
...
}
</script>
  新代码(可在IE和MF中运行):
<input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit(event)"/>
...
<script language="javascript">
function gotoSubmit(evt) {
evt = evt ? evt : (window.event ? window.event : null);
...
alert(evt); // use evt
...
}
</script>
  此外,如果新代码中第一行不改,与老代码一样的话(即 gotoSubmit 调用没有给参数),则仍然只能在IE中运行,但不会出错。所以,这种方案 tpl 部分仍与老代码兼容。


4. HTML 对象的 id 作为对象名的问题
(1)现有问题
  在 IE 中,HTML 对象的 ID 可以作为 document 的下属对象变量名直接使用。在 MF 中不能。
(2)解决方法
  用 getElementById("idName") 代替 idName 作为对象变量使用。


5. idName字符串取得对象的问题
(1)现有问题
  在IE中,利用 eval(idName) 可以取得 id 为 idName 的 HTML 对象,在MF 中不能。
(2)解决方法
  用 getElementById(idName) 代替 eval(idName)。


6. 变量名与某 HTML 对象 id 相同的问题
(1)现有问题
  在 MF 中,因为对象 id 不作为 HTML 对象的名称,所以可以使用与 HTML 对象 id 相同的变量名,IE 中不能。
(2)解决方法
  在声明变量时,一律加上 var ,以避免歧义,这样在 IE 中亦可正常运行。
  此外,最好不要取与 HTML 对象 id 相同的变量名,以减少错误。
(3)其它
  参见 问题4


7. event.x 与 event.y 问题
(1)现有问题
  在IE 中,event 对象有 x, y 属性,MF中没有。
(2)解决方法
  在MF中,与event.x 等效的是 event.pageX。但event.pageX IE中没有。
  故采用 event.clientX 代替 event.x。在IE 中也有这个变量。
event.clientX 与 event.pageX 有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。
如果要完全一样,可以稍麻烦些:
mX = event.x ? event.x : event.pageX;
  然后用 mX 代替 event.x
(3)其它
event.layerX 在 IE 与 MF 中都有,具体意义有无差别尚未试验。


8. 关于frame
(1)现有问题
  在 IE中 可以用window.testFrame取得该frame,mf中不行
(2)解决方法
  在frame的使用方面mf和ie的最主要的区别是:
  如果在frame标签中书写了以下属性:
<frame src="xx.htm" id="frameId" name="frameName" />
  那么ie可以通过id或者name访问这个frame对应的window对象
  而mf只可以通过name来访问这个frame对应的window对象
  例如如果上述frame标签写在最上层的window里面的htm里面,那么可以这样访问
ie: window.top.frameId或者window.top.frameName来访问这个window对象
mf: 只能这样window.top.frameName来访问这个window对象
  另外,在mf和ie中都可以使用window.top.document.getElementById("frameId")来访问frame标签
  并且可以通过window.top.document.getElementById("testFrame").src = 'xx.htm'来切换frame的内容
  也都可以通过window.top.frameName.location = 'xx.htm'来切换frame的内容
  关于frame和window的描述可以参见‘window与frame’文章
  以及/test/js/test_frame/目录下面的测试


9. mf中,自己定义的属性必须getAttribute()取得

10.mf中没有 parentElement parement.children 而用
parentNode parentNode.childNodes
childNodes的下标的含义在IE和MF中不同,MF使用DOM规范,childNodes中会插入空白文本节点。
  一般可以通过node.getElementsByTagName()来回避这个问题。
  当html中节点缺失时,IE和MF对parentNode的解释不同,例如
<form>
<table>
<input/>
</table>
</form>
MF中input.parentNode的值为form, 而IE中input.parentNode的值为空节点
MF中节点没有removeNode方法,必须使用如下方法 node.parentNode.removeChild(node)


11.const 问题
(1)现有问题:
  在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。
(2)解决方法:
  不使用 const ,以 var 代替。


12. body 对象
MF的body在body标签没有被浏览器完全读入之前就存在,而IE则必须在body完全被读入之后才存在


13. url encoding
  在js中如果书写url就直接写&不要写&例如var url = 'xx.jsp?objectName=xx&objectEvent=xxx';
frm.action = url那么很有可能url不会被正常显示以至于参数没有正确的传到服务器
  一般会服务器报错参数没有找到
  当然如果是在tpl中例外,因为tpl中符合xml规范,要求&书写为&
  一般MF无法识别js中的&


14. nodeName tagName 问题
(1)现有问题:
  在MF中,所有节点均有 nodeName 值,但 textNode 没有 tagName 值。在 IE 中,nodeName 的使用好象有问题(具体情况没有测试,但我的IE已经死了好几次)。
(2)解决方法:
  使用 tagName,但应检测其是否为空。


15. 元素属性
IE下 input.type属性为只读,但是MF下可以修改


16. document.getElementsByName() document.all[name] 的问题
(1)现有问题:
  在 IE 中,getElementsByName()、document.all[name] 均不能用来取得 div 元素(是否还有其它不能取的元素还不知道)。

javascript基础知识拾遗--DOM

 

一、DOM基础
1.节点(node)层次
Document--最顶层的节点,所有的其他节点都是附属于它的。
DocumentType--DTD引用(使用<!DOCTYPE>语法)的对象表现形式,它不能包含子节点。
DocumentFragment--可以像Document一样来保存其他节点。
Element--表示起始标签和结束标签之间的内容,例如<tag></tab>或者<tag/>。这是唯一可以同时包含特性和子节点的节点类型。
Attr--代表一对特性名和特性值。这个节点类型不能包含子节点。
Text--代表XML文档中的在起始标签和结束标签之间,或者CDataSection内包含的普通文本。这个节点类型不能包含子节点。
CDataSection--<![CDATA[]]>的对象表现形式。这个节点类型仅能包含文本节点Text作为子节点。
Entity--表示在DTD中的一个实体定义,例如<!ENTITY foo"foo">。这个节点类型不能包含子节点。
EntityReference--代表一个实体引用,例如&quot;。这个节点类型不能包含子节点。
ProcessingInstruction--代表一个PI。这个节点类型不能包含子节点。
Comment--代表XML注释。这个节点不能包含子节点。
Notation--代表在DTD中定义的记号。这个很少用到。


Node接口定义了所有节点类型都包含的特性和方法。

node

除节点外,DOM还定义了一些助手对象,它们可以和节点一起使用,但不是DOM文档必有的部分。
NodeList--节点数组,按照数值进行索引;用来表示和一个元素的子节点。
NamedNodeMap--同时使用数值和名字进行索引的节点表;用于表示元素特性。

 

二、使用DOM

1.访问相关结点

var oHtml = document.documentElement; //访问<html/>元素,IE5.5中会返回<body/>元素

var oHead = oHtml.firstChild; //访问<head/>元素,亦可oHtml.childNodes[0]

var oBody = oHtml.lastChild; //访问<body/>元素,亦可oHtml.childNodes[1]

实际上正式地从列表中获取子节点的方法是使用item()方法(oHtml.childNodes.item(index))。不同浏览器在判断何为Text节点上存在一些差异。某些浏览器,如Mozilla,认为元素之间的空白都是Text节点;而另一些浏览器,如IE,会全部忽略这些空白。因此在获取节点总数和相邻的时候需要注意。

2.处理特性

只有Element才能有特性。Element节点的attributes属性其实是NamedNodeMap,它提供一些用于访问和处理其内容方法:

getNamedItem(name)--返回nodename属性值等于name的节点;
removeNamedItem(name)--删除nodename属性值等于name的节点;
setNamedItem(node)--将node添加到列表中,按其nodeName属性进行索引;
item(pos)--像NodeList一样,返回在位置pos的节点;
注:请记住这些方法都是返回一个Attr节点,而非特性值。

Eg:

<p style="color:red" >Hello world!</p>
同时,假设变量oP包含指向这个元素的一个引用。于是可以这样访问id特性的值:
var sId = oP.attributes.getNamedItem("id").nodeValue;
当然,还可以用数值方式访问id特性,但这样稍微有些不直观:
var sId = oP.attributes.item(1).nodeValue;
还可以通过给nodeValue属性赋新值来改变id特性:
oP.attributes.getNamedItem("id").nodeValue="newId";

Attr节点也有一个完全等同于(同时也完全同步于)nodeValue属性的value属性,并且有name属性和nodeName属性保持同步

因为这个方法有些累赘,DOM又定义了三个元素方法来帮助访问特性,这些方法使用起来相当地方便:
getAttribute(name)--等于attributes.getNamedItem(name).value;
setAttribute(name,newvalue)--等于attributes.getNamedItem(name).value=newvalue;
removeAttribute(name)--等于attributes.removeNamedItem(name)。

3.访问指定节点

(1)getElementsByTagName()
核 心(XML) DOM定义了getElementsByTagName()方法,用来返回一个包含所有的tagName(标签名)特性等于某个指定值的元素的 NodeList。在Element对象中,tagName特性总是等于小于号之后紧跟随的名称--例如,<img />的tagName是"img"。下一行代码返回文档中所有<img />元素的列表:
var oImgs = document.getElementsByTagName("img");
把所有图形都存于oImgs后,只需使用方括号或者Item()方法(getElementsByTagName()返回一个和childNodes一样的NodeList),就可以像访问子节点那样逐个访问这些节点了。
可以使用一个星号的方法来获取document中的所有元素:
var oAllElements = document.getElementsByTagName("*");
当参数是一个星号的时候,IE6.0并不返回所有的元素。必须使用document.all来替代它。
(2)getElementsByName()
HTML DOM 定义了getElementsByName(),这用来获取所有name特性等于指定值的元素的。在IE6.0和Opera7.5中这个方法使用上还存在一些错误,它们还会返回id等于给名称定的元素,而且它们仅仅检查<input/>和<img/>元素。
(3)getElementById()
这是HTML DOM定义的第二种方法,它将返回id特性等于指定值的元素。在HTML中,id特性是唯一的。这意味着没有两个元素可以共享同一个id。毫无疑问这是从文档树中获取单个指定元素最快的方法。注意的是如果给定的ID匹配某个元素的name特性,IE6.0还会返回这个元素。这是一个bug,也是必须非常小心的一个问题。

4.创建和操作节点

createElement()、createTextNode()、appendChild()

这三个函数作用分别是创建元素节点,创建文本节点,添加节点。

Eg:

var oP = document.createElement(“p”);

var oText = document.createTextNode(“Hello World!“);

oP.appendChlid(oText);

document.body.appendChild(oP);

值得注意的是,所有的DOM操作必须在页面完全载入后才能进行。当页面正在载入时,要向DOM插入相关代码是不可能的,因为在页面完全下载到客户端机器之前,是无法完全构建DOM树。因为这个原因,必须使用onload事件句柄 来执行所有的代码。

<body onload=”createMessage()”>

....

</body>

removeChild()、replaceChild()、insertBefore()

用于更新节点,分别是删除子节点,替换子节点,插入子节点

Eg:

var oP = document.getElementById(“op”);

oP.parentNode.replaceChild(newChild, oldChild);

oP.parentNode.insertBefore(newChild, beforeChild);

oP.parentNode.removeChild(oP);

createDocumentFragment()
一 旦把节点添加到document.body(或者它的后代节点)中,页面就会更新并反映出这个变化。对于少量的更新,这是很好的,然而,当要向 document添加大量数据时,如果逐个添加这些变动,这个过程有可能会十分缓慢。为解决这个问题,可以创建一个文档碎片,把所有的新节点附加其上,然 后把文档碎片的内容一次性添加到document中.

假如想创建十个新段落。

function addMessages() {
var arrText = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth"];
var oFragment = document.createDocumentFragment();
for (var i=0; i < arrText.length; i++) {
var oP = document.createElement("p");
var oText = document.createTextNode(arrText[i]);
                    oP.appendChild(oText);
                    oFragment.appendChild(oP);
                }
                document.body.appendChild(oFragment);

对碎片使用appendChild不是把文档碎片节点本身追加到元素中,而是仅仅追加碎片中的子节点,这意味着只需要进行一次屏幕刷新,具有相当好的性能提升。

 

三、HTML DOM

1.让特性像属性一样

大部分情况下,HTML DOM元素中包含的所有特性都是可作为属性。
假设有如下图像元素:
<img src = "mypicture.jpg" border=0 />
如果要使用核心的DOM来获取和设置src和border特性,那么要用getAttribute()和setAttribute()方法:
alert(oImg.getAttribute("src"));
alert(oImg.getAttribute("border"));
oImg.setAttribute("src","mypicture2.jpg");
oImg.setAttribute("border",1);
然而,使用HTML DOM,可以使用同样名称的属性来获取和设置这些值:
alert(oImg.src);
alert(oImg.border);
oImg.src="mypicture2.jpg";
oImg.border ="1";
唯一的特性名和属性名不一样的特例是class属性,它是用来指定应用于某个元素的一个CSS类,因为class在ECMAScript中是一个保留字,在javascript中,它不能被作为变量名、属性名或都函数名。于是,相应的属性名就变成了className;
注:IE在setAttribute()上有很大的问题,最好尽可能使用属性。

2.table方法

为了协助建立表格,HTML DOM给<table/>,<tbody/>和<tr/>等元素添加了一些特性和方法。
给<table/>元素添加了以下内容:

table

<tbody/>元素添加了以下内容

tbody

<tr/>元素添加了以下内容

tr

Eg:

//create table

var oTable = document.createElement(“table”);

oTable.setAttribute(“border”, “1”);

oTable.setAttribute(“width”, “100%”);

//create the tbody

var oTBody = document.createElement(“tbody”);

oTable.appendChild(oTBody);

//create first row

oTBody.insertRow(0);

oTBody.rows[0].insertCell(0);

oTBody.rows[0].cells[0].appendChild(document.createTextNode(“Cell 1.1”));

//add table to document body

document.body.appendChild(oTable);

MVC与架构初探

 

一、 MVC模式开发

1. 什么是MVC模式

  MVC,model-view-controller,即模型-视图-控制器,MVC模式把应用程序的输入、逻辑处理和输出强制地分出三个部分。这三个核心的部分分别是模型、视图和控制器,这三个部分只负责完成各自需要处理的任务,无需涉及到其它部分的内部工作。使用MVC的目的是将模型和视图实现代码分离,也就是说令数据逻辑模型和用户界面分离,从而使同一个程序可以使用不同的表现形式和用户界面。控制器存在的目的是确保模型和视图的同步,一旦模型改变,视图也能够同步地更新,保证模型处理的逻辑结果能够及时地反映到用户界面上。

2. 模型 Model

  模型表示数据的载体,并包含着对数据进行逻辑处理的规则。模型充当着对业务流程的处理和相关业务规则的制定的角色,是MVC模式的主要核心。为了使模型能够应用于多种视图技术,由模型处理并返回的数据需要保持中立,即是说模型与数据格式无关,从而模型的代码能够被应用于多种视图。

3. 视图 View

  视图负责组织模型返回的内容,并将这些数据以指定的形式表现,代表着用户交互界面。对于Web应用程序,视图就是由HTML元素组成的界面,如jsp,php,aspx等等页面。视图将模型的数据呈现的同时,也负责从用户操作中获取数据,返回给模型和控制器处理。

4. 控制器 Controller

  控制器接受用户的输入并调用模型和视图去完成用户的需求。控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后确定用哪个视图来显示模型处理后返回的数据。控制器定义用户界面对用户输入的响应方式,负责把用户的动作转成针对于模型的操作。模型通过更新视图来反映数据的变化。

5. 应用MVC模式

clip_image001

  首先控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图模式化模型返回的数据,并通过表示层呈现给用户。

  在基于JavaEE的开发上,JSP充当着视图的角色,Servlet负责完成控制器的接收请求和分发处理的工作,JavaBean完成大部分的业务处理和逻辑判断。

  多个JSP视图可以对应一个JavaBean模型,从而可以减少代码的复制及代码的维护量,一旦JavaBean发生改变,也易于维护。

  JavaBean返回的数据可以与显示的JSP分离。这些数据可以应用任何的显示技术,例如使用JSP页面、Velocity模板或者直接产生Excel文档等。

  Web应用被分隔为三层,降低了各层之间的耦合,提供了应用的可扩展性。当某层被改变或者扩展时,对其他层的影响能够降到最低。

  Servlet把不同的JavaBean和不同的JSP视图组合在一起,完成不同的请求。因此,在控制器层包含了用户请求权限的概念。

  经典的MVC思想与Web应用的MVC思想存在一定的差别,主要是因为Web应用是一种请求/响应模式下应用,对于请求/响应应用,如果用户不对应发出请求,视图无法主动更新自己。

二、 模式与架构

1. 系统架构说明

  软件架构包括对一个软件系统组织结构的重大决策。软件架构应该能够描述系统的结构以及系统如何支持业务和质量方面的需求。软件架构是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。

  软件体系结构是构建计算机系统的基础,软件架构作为一个系统的草图,其描述的对象是直接构成系统的抽象组件。各个组件之间的连接则明确和相对细致地描述组件之间的通讯。在实现阶段,这些抽象组件被细化为实际的组件,比如具体某个类或者对象。在面向对象领域中,组件之间的连接通常用接口来实现。

  软件系统的优劣可以通过性能,可伸缩性,可靠性,可使用性,可扩展性,可维护性,可管理性,安全性等方面进行考察和衡量。

2. 三层架构

  三层架构是JavaEE开发的经典架构,三层主要是表现层、中间层(业务逻辑层)和数据访问层,其中还有一个域模型层,贯穿在三层之间。三层体系将业务规则、数据访问和合法性校验等工作放在中间层处理。客户端不直接与数据库交互,而是通过组件与中间层建立连接,再由中间层与数据库进行交互。

  中间层还可以进一步分成Web层、业务逻辑层、DAO层等等。Web层负责控制业务逻辑层与表现层的交互,调用业务逻辑层,并将业务逻辑层的数据返回给表现层作组织表现。Service层(业务逻辑层)负责实现业务逻辑,以DAO层为基础,通过对DAO组件的正面模式包装,完成系统所要求的业务逻辑。DAO层负责与持久化对象交互,该层封装了数据的增、删、查、改的操作。

3. MVC与三层架构

  MVC模式是一种设计思想,是指导软件开发过程中如何根据实际的问题组织系统的元素和抽象元素之间的关系。这种设计思想能够应用于广泛的程序开发领域。MVC设计与三层架构不冲突也不矛盾,三层架构是对MVC设计思想的一种客观体现和应用。

  在三层架构中,表现层与MVC的View视图是一致的。中间层通过进一步的划分可以体现到MVC的设计思想和分层原理,三层架构中的中间层可以进一步划分Web层、业务逻辑层和DAO层,其中Web层与MVC的Controller控制器是一致,均充当着请求转发的职责。中间层的业务逻辑层、DAO层,域模型层,以及数据服务层充当了MVC的Model,负责完成系统的主要业务流程。

  MVC作为一种设计思想,充分体现了一种如何以灵活性,低耦合,高复用的设计方案来组织系统的元素。三层架构在MVC设计思想的基础,结合实际的应用开发,对MVC设计思想中的元素进行细分再组织来实现一种富有灵活性,低耦合,高内聚,可复用的系统架构。

三、 MVC框架-Struts,Spring,Hibernate

1. Struts

  Struts是MVC模式的完整且典型的实现,对Model、View和Controller都提供了相应的实现,其具体的实现如下:

clip_image002

  Struts的Model部分由ActioinForm和JavaBean组成。其中ActionForm用于封装用户请求参数,所有的用户请求参数由系统自动封装成ActionForm对象;然后Action根据ActionForm里的请求参数处理用户请求。JavaBean则封装了底层的业务逻辑,包括数据库访问等等。

  Strus的View部分采用JSP实现。Struts提供了丰富的标签库,通过这些标签库可以最大限度地减少脚本的使用,同时,这些标签库可以实现与Model的有效交互,并增加显示功能。

  Struts的Controller由两个部分组成,系统核心控制器和业务逻辑控制器。系统核心控制器对应上图的Controller Servlet,这种控制器由Struts框架提供,继承了HttpServlet类,负责拦截所有的HTTP请求,然后根据用户请求决定是否需要调用业务逻辑控制器。业务逻辑控制器负责处理用户请求,但其本身不具有处理能力,而是通过调用Model来完成处理。业务逻辑控制器对应于上图的Action。

2. Spring

  Spring是一个轻量及的控制反转(IoC)和面向切面(AOP)的容器框架。Spring是为了解决企业应用开发的复杂性而创建的,但Spring的用途不仅限于服务器端的开发,Spring的优势能够在广泛的Java应用中得到体现。

  控制反转,或者称为依赖注入,是指创建被调用者的工作不再由调用者来完成,通常由Spring容器来完成,然后注入到调用者。Spring采用动态及灵活的方式来管理各种对象,通过面向接口编程,结合依赖注入,使对象与对象之间的具体实现互相透明。Spring内置了大量的工厂模式和单态模式来实例化和管理Spring容器中的全部对象。

  面向切面编程AOP,能够将系统组件和服务模块化,将它们声明式地应用在需要它们的地方,使得这些组件更加专注于自身的业务,而不需要知道和了解其涉及的系统服务。使开发者能以声明式、基于元数据访问企业级,合理地补充了OOP技术。

3. Hibernate

  ORM全称是Object/Relation Mapping,即对象/关系映射。ORM是一种技术,这种技术实现了面向对象的程序设计语言与关系数据库的映射,实现这种ORM技术的框架有多种,Hibernate是其中流行的一个。

  Hibernate对JDBC进行了非常轻量级的对象封装,使得程序员在开发过程中可以随心所欲地使用对象编程思维来操纵数据库。通过内置的多个Hibernate核心接口对象:Session、SessionFactory、Transaction、Query和Configuration,Hibernate能够以对象的形式获取或者保存数据。

  Hibernate通过映射文档,将Java类与数据库的表关联起来,并且有效地管理数据库的数据与Java类之间的转换,实现了数据库与程序的对象/关系映射。

4. 整合SSH实现MVC

clip_image003

  Struts和Spring都是相当优秀的MVC框架,通过Struts和Spring的整合能够弥补两个框架的不足,体现更强大的功能。Struts和Spring有三种整合方案,不同的方案实现程序的解耦程度不同。本人偏向于使用Spring的RequestProcessor来代替Struts的请求转发,这些通过Spring能控制请求的转发,能够使Action部署在Spring的容器中,由Spring统一管理Action和其他的业务对象,充分地利用了Spring的依赖注入的特性,同时也降低了Struts和Spring的耦合程度。Struts提供了控制器和表示层的控制,Spring负责管理所有的中间层组件对象。

  DAO层通过对Hibernate提供的核心接口对象进行封装使用,充分地利用了Hibernate提供了ORM的持久化技术,通过面向对象的方式与数据库进行交互。Spring提供的DAO组件进一步简化了持久层访问,通过对SessionFactory的依赖注入简化了Session的控制。

[转]针对Web系统常用的功能测试方法浅析

功能测试就是对产品的各功能进行验证,根据功能测试用例,逐项测试,检查产品是否达到用户要求的功能。针对Web系统的常用测试方法如下:
  1. 页面链接检查:每一个链接是否都有对应的页面,并且页面之间切换正确。
  2. 相关性检查:删除/增加一项会不会对其他项产生影响,如果产生影响,这些影响是否都正确。
  3. 检查按钮的功能是否正确:如update、cancel、delete、save等功能是否正确。
  4. 字符串长度检查:输入超出需求所说明的字符串长度的内容,看系统是否检查字符串长度,会不会出错。
  5. 字符类型检查:在应该输入指定类型的内容的地方输入其他类型的内容(如在应该输入整型的地方输入其他字符类型),看系统是否检查字符类型,会否报错。
  6. 标点符号检查:输入内容包括各种标点符号,特别是空格、各种引号、回车键。看系统处理是否正确。
  7. 中文字符处理:在可以输入中文的系统输入中文,看会否出现乱码或出错。
  8. 检查带出信息的完整性:在查看信息和update信息时,查看所填写的信息是不是全部带出,带出信息和添加的是否一致。
  9. 信息重复:在一些需要命名,且名字应该唯一的信息输入重复的名字或ID,看系统有没有处理,会否报错,重名包括是否区分大小写,以及在输入内容的前后输入空格,系统是否作出正确处理。
  10. 检查删除功能:在一些可以一次删除多个信息的地方,不选择任何信息,按”delete”,看系统如何处理,会否出错; 然后选择一个和多个信息,进行删除,看是否正确处理。
  11. 检查添加和修改是否一致:检查添加和修改信息的要求是否一致,例如添加要求必填的项,修改也应该必填; 添加规定为整型的项,修改也必须为整型。
  12. 检查修改重名:修改时把不能重名的项改为已存在的内容,看会否处理,报错。同时,也要注意,会不会报和自己重名的错。
  13. 重复提交表单:一条已经成功提交的纪录,back后再提交,看看系统是否做了处理。
  14. 检查多次使用back键的情况:在有back的地方,back,回到原来页面,再back,重复多次,看会否出错。
  15. search检查:在有search功能的地方输入系统存在和不存在的内容,看search结果是否正确。如果可以输入多个search条件,可以同时添加合理和不合理的条件,看系统处理是否正确。
  16. 输入信息位置:注意在光标停留的地方输入信息时,光标和所输入的信息会否跳到别的地方。
  17. 上传下载文件检查:上传下载文件的功能是否实现,上传文件是否能打开。对上传文件的格式有何规定,系统是否有解释信息,并检查系统是否能够做到。
  18. 必填项检查:应该填写的项没有填写时系统是否都做了处理,对必填项是否有提示信息,如在必填项前加*
  19. 快捷键检查:是否支持常用快捷键,如Ctrl+C Ctrl+V Backspace等,对一些不允许输入信息的字段,如选人,选日期对快捷方式是否也做了限制。
  20. 回车键检查:在输入结束后直接按回车键,看系统处理如何,会否报错。

来源:中国IT实验室

SSH配置错误及解决方案拾遗

问题描述:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'SessionFactory' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V  
Caused by: java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)

解决方案:包冲突,去掉asm-2.2.3.jar成功启动

 

问题描述:
ERROR   [org.springframework.web.context.ContextLoader]   -   Context   initialization   failed
org.springframework.beans.factory.BeanCreationException:   Error   creating   bean   with   name   'sessionFactory '   defined   in   ServletContext   resource   [/WEB-INF/applicationContext.xml]:   Instantiation   of   bean   failed

解决方案:工程没有添加dom4j.jar这个包

 

问题描述:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'dataSource' defined in file [D:\HS_Test\WebRoot\WEB-INF\applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError:
org/apache/commons/pool/impl/GenericObjectPool
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool

解决方案:原因是工具myEclipse在建立SessionFactory的时候,少加一个包 commons-pool-1.3.jar 补上去问题解决。

 

问题描述:
exception: cannot find actionmappings or actionformbeans collection

原因及解决方案:
1.Web.xml未初始化问题
少了一个<load-on-startup>0</load-on-startup>
这样开始是没有加载这个类,就没有初始化struts-config.xml,所以找不到
actionmappings or actionformbeans collection。
2.struts-config.xml问题
3.jar包不全或版本不对
也有可能导致这个问题,从stuts例子里面拷过来就好了。struts1.1自带共10个jar包。
4.编译问题
有可能Action和Formbean没有编译,也会有这个问题,检查一下有没有对应的class文件就好。class路径不对可能也会导致这个问题。
5.版本问题
用的servlet容器和struts版本不对或这两个版本协调有问题。

 

问题描述:
DispatchMapping[/calc] does not define a handler property

解决方案:在struts-config.xml中配置了一个parameter="method",这个应该是在使用DispatchAction的时候忘了在action的配置中要加上parameter的属性设置。

javascript基础知识拾遗--事件

 

1.事件流

  事件流有两种,分别是冒泡型事件流和捕获型事件流。冒泡型事件流是按照DOM的层次结构从里层向外层进行上溯,事件按照从最特定的事件目标到最不特定的事件目标的顺序进行触发,如下图中,从target一直向上触发事件到observer,以至到root。捕获型事件流和冒泡型是相反的事件触发过程,即在捕获型事件中,事件从最不精确的对象开始触发,然后到最精确的对象。

clip_image002

  在IE中,支持冒泡型事件流。而DOM则同时支持两种事件模型(这就是为什么在DOM的监听函数中有第三个参数来识别事件处理函数所在的事件流),但是捕获型事件先发生,两种事件触及DOM中所有对象的,从document对象开始,也在document对象结束,也就是说事件的目标会连续接收两次事件,一次在捕获过程中,另一次在冒泡过程中,当然两次的事件处理函数可以不同。DOM事件模型最独特的性质是,文本节点也触发事件(在IE中不会),即点击文本,文本可以产生事件。

 

2.事件处理/监听函数

(1)IE

  在IE中,每个元素和window对象都有两个方法:attachEvent()和detachEvent()。顾名思义,attachEvent()用来给一个事件附加事件处理函数,而detachEvent()用来将事件处理函数分离出来。每个方法都有两个参数:要分配的事件处理函数的名字(例如:onclick)及一个处理函数。

[Object].attachEvent(“name_of_event_handler”, fnHandler);

[Object].detachEvent(“name_of_event_handler”, fnHandler);

Eg:

var fnClick = function() {

alert(“Clicked!”);

};

var oDiv = document.getElementById(“odiv”);

oDiv.attachEvent(“onclick”, fnClick); //oDiv.onclick = fnClick 亦可

oDiv.detachEvent(“onclick”, fnClick);

  可以使用多个attachEvent()来为一个事件添加多个处理函数,事件处理函数总是按照添加它们的顺序来进行调用。

 

(2)DOM

  DOM方法addEventListener()和removeEventListener()用来分配和移除事件处理函数。与IE不同,这些方法需要三个参数:事件名称,要分配的函数和处理函数是用于冒泡阶段还是捕获阶段。如果事件处理函数是用于捕获阶段,则第三个参数为true;用于冒泡阶段,则第三个参数为false。用removeEventListener()移除事件的时候要注意正确选择在冒泡阶段还是捕获阶段。如果添加在冒泡阶段中的处理函数,尝试在捕获阶段删除,这不会产生错误,但函数是不会删除的。

[Object].addEventListener(“name_of_event”, fnHandler, bCapture);

[Object].removeEventListener(“name_of_event”, fnHandler, bCapture);

Eg:

var fnClick = function() {

alert(“Clicked!”);

};

var oDiv = document.getElementById(“oDiv”);

oDiv.addEventListener(“click”, fnClick, false);

oDiv.removeEventListener(“click”, fnClick, false);

  与IE一样,也可以为一个事件添加多个事件处理函数,其执行顺序还是会按照指定它们的顺序进行调用。

 

3.事件对象

(1)获取事件对象

  事件对象包含了事件发生的信息,事件对象只在发生事件时才被创建,且只有事件处理函数才能访问。所有事件处理函数执行完毕后,事件对象就被销毁了。

在IE中,事件对象是window对象的一个属性event。事件处理函数必须这样访问事件对象:

oDiv.onclick = function() {

var oEvent = window.event;

}

  尽管它是window对象的属性,event对象还是只能在事件发生时访问。所有的事件处理函数执行完毕后,事件对象就被销毁了。

  在DOM标准中,event对象必须作为唯一的参数传给事件处理函数,做法如下:

oDiv.onclick = function() {

var oEvent = arguments[0];

}

或者可以直接命名参数,访问就更方便了:

oDiv.onclick = function(oEvent) {

}

 

(2)IE与DOM的事件对象相似总结

获取事件类型:var sType = oEvent.type;

获取按键代码(keydown/keyup事件):var iKeyCode = oEvent.keyCode;

检测Shift、Alt、Ctrl键:oEvent.shiftKey, oEvent.altKey, oEvent.ctrlKey

获取客户端坐标:oEvent.clientX, oEvent.clientY

获取屏幕坐标:oEvent.screenX, oEvent.screenY

 

(3)IE与DOM的事件对象区别总结

<1>获取事件的目标

目标是指触发事件的目标,IE目标只能是元素、文档或者窗口,而DOM兼容的浏览器也允许文本作为目标。

在IE中,目标包含在event对象的srcElement属性中:

var oTarget = oEvent.srcElement;

在DOM兼容的浏览器中,目标包含在target属性中:

var oTarget = oEvent.target;

<2>获取字符代码

Ie和DOM都支持event对象的keyCode属性,它会返回按下按键的数值代码。如果按键代表一个字符(非Shift、Ctrl、Alt等),IE的keyCode返回字符代码(等于它的Unicode值):

var iCharCode = oEvent.keyCode;

在DOM兼容的浏览器中,按键的代码和字符代码会有一个分离。对于按键是字符的处