编写面向对象的 JavaScript 第三部分






4.64/5 (18投票s)
2003年12月9日
11分钟阅读

100960

1098
使用 Cfx 开发 JavaScript 控件类库。
摘要
本系列的第一部分描述了 JavaScript 的面向对象特性。第二部分在此基础上进一步介绍了支持类继承的框架。在本最终章中,我们将使用 Cfx
来构建一个 JavaScript 控件类层次结构,为 ASP.NET 用户控件提供丰富的客户端行为。
引言
Web 控件代表网页的一部分,通常提供用户交互功能,如文本框、单选按钮、复选框等。Web 控件也可以是静态的。将页面细分为控件可以使页面成为 Web 控件的容器,从而提高抽象性,并增强重用性和可扩展性。
使用 ASP.NET,您可以利用传统的 HTML 元素或 ASP.NET 控件。您可以通过定义自己的用户控件来增强它们。最终,ASP.NET 会将所有控件渲染为客户端浏览器解释的 HTML 和 JavaScript。当然,您可以自己编写网站并使用默认的 ASP.NET 行为。然而,这在所有情况下都不可行,并且会限制用户体验。使用 Cfx
,您现在可以通过编写面向对象的 JavaScript 来实现对客户端脚本的更大控制和丰富性。
注意 - ASP.NET 和 Web 控件的详细解释超出了本文的范围,因为有许多关于 ASP.NET 的优秀书籍和文章。本文的目的是展示 Cfx
如何用于构建利用 ASP.NET 来创建丰富客户端的类。
策略
利用 ASP.NET 控件命名
ASP.NET 在将控件渲染为 HTML 元素时,使用 id
和 name
属性应用了一致的命名方案。ASP.NET 使用控件名称前加上其容器名称,并通过下划线分隔来命名 HTML 元素。例如,在附带的演示中,ASP.NET CfxText
用户控件封装了 ASP.NET 文本框控件,并为其提供了标识符 txtField
。
<asp:TextBox id="txtField" runat="server" maxlength="255"></asp:TextBox>
ASP.NET CfxText
用户控件的 id 是 txtName
,随后被 id 为 CfxWebFormOption1
的 CfxWebFormOptions
控件封装。ASP.NET 为 HTML 文本元素生成的 id 使用最外层容器的标识符,后跟分隔符,然后是下一个容器的名称,最后是控件标识符。
<input id="CfxWebFormOptions1_txtName_element" type="text"
maxlength="255" class="LgNorm" style="width:224px;" />
通过利用这种命名约定,我们可以逻辑地分组控件元素,并开发一个 JavaScript 中的 ASP.NET 控件包装器类层次结构,为 ASP.NET 用户控件提供客户端行为支持。
注意 - ASP.NET 2.0 使用美元符号 ($) 作为分隔符。美元符号消除了下划线分隔符可能在标识符中使用的歧义。Cfx.Dom.GetElementTerms
使用正则表达式来处理分割 ASP.NET 标识符为元素术语时的下划线歧义。
JavaScript 控件类层次结构
CfxControl
概念上代表页面上使用的用户控件类的集合。这些类代表布局描述及其客户端行为定义。布局描述使用 ASP.NET 定义,并保存在附带的 .aspx
和 .cs
文件中。客户端行为用 JavaScript 编写在相应的 .js
文件中。因此,客户端行为与其布局是分离的。请注意,我们几乎实现了渲染文件和 JavaScript 文件之间的一对一对应关系。附加的 JavaScript 文件包含在 CfxControl
和 CfxInput
类中定义的通用基类定义和实现。
在 JavaScript 控件类层次结构的顶层是 Object
类,紧接着是 CfxControl
类,它是所有 JavaScript 控件的根。CfxWebFormOptions
控件是 WebForm1
页面上输入控件的容器类。有三个输入控件从 CfxInput 类派生出特定的功能。CfxText
类代表文本输入控件。CfxSelect
类代表单选下拉列表,CfxLabel
包装了 ASP.NET Label
控件。CfxRadioSet
代表一组单选按钮元素。CfxHistoryOptions
类包含 CfxRadioSet
和 CfxSelect
类,提供了一个用于进行“历史”选择的控件。
提供了一对不属于 CfxControl
继承体系的辅助类。CfxError
类扩展了 Error
类,增加了抛出异常的框架对象属性,以及用于 JavaScript event
对象的 CfxEvent
包装器。
图 1. CfxControl 类层次结构。
JavaScript 客户端控件
CfxControl
CfxControl
类是所有派生控件的基类,封装了所有控件继承的属性。name
属性是控件的名称。将控件视为文档元素容器,name
属性代表容器的名称。parent
属性是父 CfxControl
。items
属性是一个文档元素数组,这些元素的 id
属性包含控件名称。
图 2. ASP.NET 控件标识符和术语。
例如,当创建名为CfxWebFormOptions1
的 CfxWebFormOptions
控件时,文档中所有 id
属性包含字符串 CfxWebFormOptions1
的元素和图像都会被添加到 CfxControl
的 items
数组中。
CfxInput
CfxInput
类是输入控件的基类。此类将输入控件定义为具有一个输入元素属性 element
和一个指示输入控件错误状态的图像属性 image
。它定义了几个基本方法来操作 image
属性,以及 Initialize
和 Validate
方法。CfxInput
没有被定义为抽象类,并且可以被实例化。这对于需要通用输入控件对象的情况很有用。
CfxText
CfxText
控件包装文本输入元素,并根据 regExp 正则表达式属性验证其输入。该控件还提供了监听 onkeypress
事件的能力,检测回车键以启动验证。用户可以通过 CfxText
控件的 SubmitHandler
方法提供一个事件处理程序,以应对回车键按下。
this.txtName.SubmitHandler( OnEnterKey );
图 3. 将 txtName 控件的 SubmitHandler 设置为 OnEnterKey 处理程序。
如果提供了提交处理程序,在文本输入元素的 onkeypress
事件期间,执行将转移到 CfxText
控件的 OnEnterKey
方法。CfxEvent
类包装了 JavaScript 事件对象,并检测回车键事件。如果按下了回车键事件,OnEnterKey
方法会调用 CfxText
提交处理程序。
function OnEnterKey( evnt )
{
var cfxEvent = new CfxEvent( evnt );
if ( cfxEvent.IsEnterKey() )
{
// Find the instance from the event source.
var thisObj = cfxEvent.FindInstance( CfxText );
// Call CfxText control submit handler.
return thisObj.submit;
}
else
return true;
}
图 4. CfxText OnEnterKey 事件处理程序。
由 onkeypress
事件触发的 OnEnterKey
方法中的 this
引用不指向 CfxText
实例,而是指向事件源元素。FindInstance
用于获取与源元素关联的 CfxText
实例。为了使 FindInstance
能够确定实例,CfxText.instance
标志在类定义中被设置为 true
。
CfxSelect
CfxSelect
控件包装了一个单选下拉列表输入元素,提供了向下拉列表中添加和删除元素的方法。CfxSelect
还提供了访问器方法来获取选定的文本和值,以及选定元素的索引。
CfxLabel
CfxLabel
包装了 ASP.NET Label
控件,提供了一种交互式的方式来更改页面文本。ASP.NET 将其标签渲染为 span
元素,CfxLabel
通过其 id
属性定位它,该 id
属性是通过连接其祖先控件的名称构建的。Value
访问器更新 span 元素的内部文本。CfxLabel
可用于增强错误通知的显示,而不是使用 alert。
CfxRadioSet
CfxRadioSet
控件包装了一组单选输入元素。ClickHandler
方法设置单选按钮元素的 onclick
事件处理程序。GetElement
返回 CfxRadioSet
的单选按钮元素,通过单选按钮索引。GetIndex
返回 CfxRadioSet
单选按钮元素的索引。CfxRadioSet
重写了从 CfxControl
继承的 GetItems
方法,因为单选按钮元素是通过 name
属性分组的,而基类方法使用 id
属性来查找元素。GetSelected
方法返回选中的单选按钮元素,Length
返回 CfxRadioSet
中的单选按钮元素数量。
CfxHistoryOptions
此控件提供了模拟选择历史标准的元素集客户端功能。此控件为 CfxRadioSet
控件以及与单选按钮相关的控件提供功能。
图 5. CfxHistoryOptions 用户界面。
CfxHistoryOptions
将控件分组到一个 CfxRadioSet
控件中,该控件包含单选按钮元素列表以及单选按钮的关联。例如,radioSet
属性包含单选按钮元素:radDefault
、radByMonth
和 radByYear
。radByMonth
单选输入元素的关联是名为 radByMonth_selByMonth
的 CfxSelect
控件,而 radByYear
单选输入元素的关联是 radByYear_selByYear
控件。radDefault
单选元素没有关联。单选元素的 value
属性标识了关联,因此 CfxHistoryOptions
类可以分组包含该值的元素。在这种情况下,radByMonth
单选输入关联由 radByMonth
单选按钮和 radByMonth_selByMonth
下拉列表组成。
图 6. 分组单选元素关联控件。
在初始化过程中,CfxHistoryOptions
为单选按钮集建立了一个 onclick
事件处理程序。当单击单选按钮元素时,会调用 OnRadioClick
处理程序,该处理程序会更改单选按钮关联的禁用状态。请注意,处理程序使用 CfxEvent
类的 FindInstance
方法从事件源元素获取 CfxHistoryOptions
类的实例。最后,Validate
方法会验证选择。
Cfx 包装器类
CfxError
此包装器类增强了 JavaScript Error
对象,增加了一个属性,该属性引用抛出异常的 Cfx
类的实例。正如您将在演示中看到的,抛出的 CfxError
对象用于识别并更改正在抛出异常的 CfxText 对象的状态。
CfxEvent
CfxEvent
类是 JavaScript event
对象的包装器。event
对象包含有关 JavaScript 事件的信息,并传递给 CfxControl
事件处理程序。CfxEvent
隐藏了 event
对象的浏览器差异,提供了一致的接口。在演示中,CfxControl
事件处理程序负责检测回车键事件、单选按钮选中事件和提交按钮单击事件。处理程序创建一个 CfxEvent
对象来包装 event
对象,并调用 FindInstance
将触发事件的元素转换为其对应的 CfxControl
实例。
前端代码
控件的最终实现发生在 WebForm1
页面的 JavaScript 中。当 WebForm1
加载时,onload
事件调用页面的 OnLoad
函数,实例化一个新的 CfxWebFormOptions
控件并将其赋值给 formOptions
。SubmitHandler
方法分配 OnSubmit
事件处理程序,该处理程序在控件提交按钮单击事件发生或从文本控件按下回车键时调用。OnSubmit
然后调用 Validate
方法,并在验证成功时执行回发。
<script type="text/javascript">
function OnLoad()
{
formOptions = new CfxWebFormOptions( "CfxWebFormOptions1" );
formOptions.SubmitHandler( OnSubmit );
}
function OnSubmit()
{
if ( formOptions.Validate() == true )
Cfx.Dom.PostBack();
else
return false;
}
<script>
<body topmargin="0" leftmargin="0" marginheight="0" marginwidth="0"
onload="OnLoad();">
图 7. WebForm1 页面前端代码现已简化为例程。
基准测试
Cfx
的性能出奇地好。演示程序包含显示页面加载和提交性能的警报。对于 2GHz 的系统,IE 6.0 的每次任务时间不到 35 毫秒,Mozilla 1.4 的时间不到 50 毫秒。
演示
ControlDemo
演示程序展示了如何使用 Cfx
设计和实现控件类层次结构。此应用程序的方法是让 ASP.NET 负责页面布局,JavaScript 负责客户端用户交互。
此应用程序的 URL 为 https:///JsOOP/ControlDemo/WebForm1.aspx。将源代码复制到您 https:// 主目录的 JsOOP/ControlDemo 子目录中。您可以使用 Visual Studio 创建 ASP.NET 项目,或者使用“控制面板”中的“管理工具”中的 IIS 管理工具。此演示已在运行时版本 1.0 和 1.1 上进行了测试。
结论
本系列关于 JavaScript 面向对象编程的文章旨在填补客户端脚本开发领域的一个惊人空白。脚本常常被写成过程式附加项,而不是可重用对象。这很大程度上是因为 JavaScript 书籍未能充分解释该语言的面向对象功能,并且缺乏对类继承的内在支持。另一个促成因素是 ASP.NET 本身,其编程模型建议将 JavaScript 编写为 ASP.NET 控件的过程式附加项。
Cfx
提供了一个易于使用的编码模式,用于在 JavaScript 中编写类层次结构。编写面向对象脚本的结果是更丰富的客户端功能,通过让 JavaScript 承担更多的 UI 管理职责。虽然 ASP.NET 定义了页面布局,但代码隐藏变得微不足道,从而使服务器端代码能够更好地专注于业务逻辑。
另一方面,让 ASP.NET 控件发出 JavaScript 可能也很有用。使用 Cfx
,您仍然可以以面向对象的方式构建脚本。Cfx
非常灵活,让您可以选择最适合您的部署方式。
Cfx
的另一个优点是它让您作为 Web 开发人员能够保留对代码更大的控制权。ASP.NET 功能强大,但过度依赖任何单一供应商的工具,在针对多个平台和浏览器时可能会带来麻烦。Cfx
由于其面向对象,因此可以轻松地将特殊性隔离在 fachada 和包装器中。虽然演示是为 ASP.NET 编写的,但 Cfx
是一个 JavaScript 框架,不特定于 ASP.NET。
我希望 JavaScript 类框架能够提高客户端脚本的质量,推广丰富的 Web 界面,减少供应商依赖,并促进可重用 JavaScript 类的集合的构建。
修订历史
- 版本 1.0 -- 初始版本
- 版本 1.1 -- 修复了内容错误。