Ext JS在其历史上第一次进行了彻底的类体系的重构。新的架构应用到了几乎每一个Ext JS 4.x的类中,因此在使用它编写代码之前理解它是非常必要的。
本手册适用于所有在Ext JS 4.x中创建新类和继承已有类的程序员。分为四部分:
- 第I部分:“Overview”分析了鲁棒的类体系的需求
- 第II部分:“Naming Conventions”讨论命名类、方法、属性、变量和文件的最佳实践
- 第III部分:“Hands-on”给出了代码示例
- 第IV部分:“Errors Handling & Debugging”给出了一些处理异常的技巧
I. Overview
Ext JS 4包含超过300个类。目前Ext JS社区聚集了来自世界各地各种编程背景的二十多万名开发人员。对于这种规模的框架,我们在提供一般的编码架构方面面临巨大挑战:
- 便于学习
- 快速开发,便于调试,便于部署
- 条理清晰,高可扩展性,高可维护性
JavaScript是一种无类型,面向原型(prototype-oriented)语言。因此,JavaScript本质上最强大的特性之一就是灵活性。它可以通过多种方式、不同编码风格和技术完成同一任务。但是高灵活性是以不可预测性为代价的。如果没有统一的结构,JavaScript代码是难以理解、维护和重用的。
另一方面,基于类(class-based)的编程仍然是OOP最常见的模型。基于类的语言(class-based languages)通常是强类型的(strong-typing),支持封装,且有标准的编码规范。通过使程序员们遵循一系列的规则,代码变得更加可预测、可扩展。然而,它们没有JavaScript等语言的动态性。
每种方式都有各自的优点和缺点。我们是否能够只取它们的优点,隐藏它们的缺点呢?答案是肯定的,我们在Ext JS 4中实现了这种解决方案。
II. Naming Conventions
代码中的classes、namespaces和文件名使用一致的命名规范提高了代码的条理性、结构性和可读性。
1) Classes
类名只能含有字母和数字字符。多数情况下应该避免使用数字,除非这些数字属于某个专业术语。不要使用下划线、连字符和任何其他的非字母数字的字符。例如:
- MyCompany.useful_util.Debug_Toolbar是不推荐使用的
- MyCompany.util.Base64是可接受的
类名应该使用点符号(.)分组到相应的包中。至少应该有一个唯一的顶层命名空间。例如:
MyCompany.data.CoolProxy MyCompany.Application
顶层命名空间和类名应该使用驼峰命名法,其他的应该全部使用小写。例如:
MyCompany.form.action.AutoLoad
非Sencha发布的类不应该使用Ext作为顶层命名空间。
缩写词也应该遵循驼峰命名法。例如:
- 使用Ext.data.JsonProxy代替Ext.data.JSONProxy
- 使用MyCompany.util.HtmlParser代替MyCompary.parser.HTMLParser
- 使用MyCompany.server.Http代替MyCompany.server.HTTP
2) Source Files
类名直接映射到该类存储的路径。因此,每个类必然只对应一个文件。例如:
- Ext.util.Observable存储在path/to/src/Ext/util/Observable.js
- Ext.form.action.Submit存储在path/to/src/Ext/form/action/Submit.js
- MyCompany.chart.axis.Numeric存储在path/to/src/MyCompany/chart/axis/Numeric.js
path/to/src是你的应用程序的类所在目录。所有的类都应该存储在这个根目录中,而且应该分为适当的包以便于开发、维护和部署。
3) Methods and Variables
- 类似于类名,方法名和变量名也只能使用字母数字(alphanumeric)字符。多数情况下,应该避免使用数字,除非数字属于某个专业术语。不要使用下划线、连字符和其他非字母数字(nonalphanumeric)字符。
- 方法名和变量名也应该遵循驼峰命名法。这也适用于缩写词。
- 例子
- 合理的方法名:encodeUsingMd5();getHtml()代替getHTML();getJsonResponse()代替getJSONResponse() ;parseXmlContent()代替parseXMLContent()
- 合理的变量名:isGoodName、base64Encoder、xmlReader、httpServer
4) Properties
- 类变量(class property)命名遵循与方法名变量名相同的规范,除了静态常量(static constant)的情况。
- 静态类常量应该全部采用大写字符。例如:
- Ext.MessageBox.YES = "Yes"
- Ext.MessageBox.NO = "No"
- MyCompany.alien.Math.PI = "4.13"
- Ext.MessageBox.YES = "Yes"
III. Hands-on(动手实践)
1. 声明(Declaration)
1.1) 老方法(The Old Way)
如果你用过之前版本的Ext,那么你可能对使用Ext.extend创建类比较熟悉:
var MyWindow = Ext.extend(Object, { ... });
这种方法便于创建一个继承自其他类的新类。除了直接继承,没有支持类创建其他方面的API,如configuration、statics和mixins。我们下面简短地回顾一下这些项目。
我们看一个例子:
My.cool.Window = Ext.extend(Ext.Window, { ... });
在这个例子中,我们想要namespace一个新类,使它继承自Ext.Window。需要注意两点:
1.赋值之前,My.cool必须是一个存在的对象
2.在引用Ext.Window之前,它必须加载到页面上
第一项通常采用Ext.namespace(即Ext.ns)来解决。该方法递归地遍历对象/属性树(object/Property tree),并在不存在的情况下创建它。烦人的是,你需要在Ext.extend之前加上它们。
Ext.ns('My.cool'); My.cool.Window = Ext.extend(Ext.Window, { ... });
第二个问题却不易解决,因为Ext.Window可能依赖于许多其他的类,它直接或间接地继承自这些类,同样的,这些被依赖的类可能又依赖更多的其他的类。鉴于此,采用Ext JS 4之前版本写的程序通常include整个库(ext-all.js),即使仅仅用到了很少一部分。
1.2)新方法(The New Way)
Ext JS 4消除了上述缺点,创建类时只要记住Ext.define一个方法即可。它的基本语法如下:
Ext.define(className, members, onClassCreated);
- className:类名
- members是以key-value对格式描述的类成员集合
- onClassCreated是一个可选的回调函数(callback),当该类的所有依赖准备完毕且类创建完成时调用。由于创建类的新的异步特质(new asynchronous nature),这个回调函数在很多情况下有用。这将在Section
IV中进一步讨论。
示例:
Ext.define('My.sample.Person', { name: 'Unknown', constructor: function(name) { if (name) { this.name = name; } return this; }, eat: function(foodType) { alert(this.name + " is eating: " + foodType); return this; } }); var aaron = Ext.create('My.sample.Person', 'Aaron'); aaron.eat("Salad"); // alert("Aaron is eating: Salad");
我们使用Ext.create()方法创建了一个My.sample.Person的实例。我们可以使用new创建(new My.sample.Person())。但是我们推荐使用Ext.create,因为它允许你使用动态载入(dynamic loading)。更多关于dynamic loading的信息请参考Get
Started Guide。
2.Configuration
在Ext JS 4中,我们引入了专用的config属性,使得类在创建之前使用强大的Ext.class预处理器处理。包含以下特征:
- Configurations完全是从其他类成员封装而来
- 如果类中没有定义,每个config属性的Getter和setter方法自动生成到类的prototype。
- 每个config属性会生成一个apply方法。自动生成的setter方法在设置值之前调用apply方法。如果你要在设置值(setting the value)之前执行定制的逻辑,可以复写(override)一个config属性的apply方法。如果apply没有返回值,则setter将不会设置值。例如下面的applyTitle:
这里是一个例子:
Ext.define('My.own.Window', { /** @readonly */ isWindow: true, config: { title: 'Title Here', bottomBar: { enabled: true, height: 50, resizable: false } }, constructor: function(config) { this.initConfig(config); return this; }, applyTitle: function(title) { if (!Ext.isString(title) || title.length === 0) { alert('Error: Title must be a valid non-empty string'); } else { return title; } }, applyBottomBar: function(bottomBar) { if (bottomBar && bottomBar.enabled) { if (!this.bottomBar) { return Ext.create('My.own.WindowBottomBar', bottomBar); } else { this.bottomBar.setConfig(bottomBar); } } } });
这里是一个使用上述定义的例子:
var myWindow = Ext.create('My.own.Window', { title: 'Hello World', bottomBar: { height: 60 } }); alert(myWindow.getTitle()); // alerts "Hello World" myWindow.setTitle('Something New'); alert(myWindow.getTitle()); // alerts "Something New" myWindow.setTitle(null); // alerts "Error: Title must be a valid non-empty string" myWindow.setBottomBar({ height: 100 }); // Bottom bar's height is changed to 100
3.statics
static成员可以使用statics config来定义:
Ext.define('Computer', { statics: { instanceCount: 0, factory: function(brand) { // 'this' in static methods refer to the class itself return new this({brand: brand}); } }, config: { brand: null }, constructor: function(config) { this.initConfig(config); // the 'self' property of an instance refers to its class this.self.instanceCount ++; return this; } }); var dellComputer = Computer.factory('Dell'); var appleComputer = Computer.factory('Mac'); alert(appleComputer.getBrand()); // using the auto-generated getter to get the value of a config property. Alerts "Mac" alert(Computer.instanceCount); // Alerts "2"
IV. Errors Handling & Debugging
Ext JS 4提供了一些帮助调试和错误处理的特性。
- 可以使用Ext.getDisplayName()来获取任意方法的显示名(display name)。例如:
throw new Error('['+ Ext.getDisplayName(arguments.callee) +'] Some message here');
- 若使用基于WebKit浏览器(Chrome或Safari),用Ext.define()定义的类的方法抛出错误时,可以在调用栈(call stack)中看到方法名和类名。例如,下面是Chrome里的示例:
See Also
- Dynamic Loading and the New Class System
- Classes in Ext JS 4: Under the Hood
- The Class Definition Pipeline
英文原文链接:The Class System