综述
Ext JS拥有大量的类。到目前为止,我们有超过200万的开发人员,他们有不同的背景,来自不同的地方。面对这么大规模的开发人员,我们想要提供一个如下特点的通用代码框架,变得有些困难:
*简单易学
*快速开发,容易调试,轻松部署。
*结构化,可扩展,可维护。
因为JavaScript是一个无类的,面向原型(参见:https://en.wikipedia.org/wiki/Prototype-based_programming)的语言,所以它最强大的特点之一是灵活性。通过使用很多种编程风格和技术,我们能够为任何问题提供多种解决方案。然而,代价是,这些解决方案是不确定的。没有一个统一的结构,JavaScript代码将变得很难理解,维护和重用。
从另一方面来说,基于类的编程(参见:https://en.wikipedia.org/wiki/Class-based_programming)仍是最流行的面向对象编程的编程模式。基于类的编程语言通常需要强类型,封装和标准的编码约定。通过让开发人员遵循大量的规则,经过一段时间后,代码似乎会可维护,可扩展,可计量。然而这种模式没有JS开发的动态特性。
每种方法都有好跟坏的一面,但我们能否保持两种模式下好的方面,而去掉坏的方面呢?答案是肯定的,你将在Ext JS里找到解决方案。
命名约定
坚持使用基于类,命名空间和文件名的命名约定,可以让你的代码更有组织性,结构化和可读性。
类
类名或许只包含数字跟字母。数字被允许但不被鼓励使用,除非它们属于专业技术术语。不要使用下划线,连字号,或其它非数字与字母的字符。例如:
MyCompany.useful_util.Debug_Toolbar isdiscouraged
MyCompany.util.Base64 is best
类名应该被划分到合适的组,这些组使用类似对象属性的点记法(即.)来命名。在类名前,至少要有一个顶级的命名空间。例如:
MyCompany.data.CoolProxy
MyCompany.Application
顶级的命名空间和实际的类名应该使用大骆驼拼写法。其它的使用小写字母。例如:
MyCompany.form.action.AutoLoad
非Sencha发布的类不能使用Ext作为顶级命名空间。
首字母缩写词也应该遵循上面列出的大骆驼拼写法。例如:
Ext.data.JsonProxy instead ofExt.data.JSONProxy
MyCompany.util.HtmlParser instead ofMyCompary.parser.HTMLParser
MyCompany.server.Http instead ofMyCompany.server.HTTP
源码文件
类的命名直接与源码文件存放路径相匹配。这样一来,每个类需要单独存放成一个文件。例如:
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是你程序的类文件存放的路径。为了得到最好的开发,维护与部署体验,所有的类都应该被存放在这个通用的根目录下,并且被分组到合适的命名空间。
方法和变量
与类的命名方式类似,方法与变量的名称也只能包含数字或字母型字符。数字被允许但不被鼓励使用,除非它们属于专业技术术语。不要使用下划线,连字号,或其它非数字与字母的字符。
方法与变量名应该总是使用小骆驼拼写法。这同样应用于首字母缩写词。
示例
合法的方法名:
encodeUsingMd5()
getHtml() instead of getHTML()
getJsonResponse() instead ofgetJSONResponse()
parseXmlContent() instead ofparseXMLContent()
合法的变量名:
var isGoodName
var base64Encoder
var xmlReader
var httpServer
属性
属性命名遵循两样的命名规范,除非它们是静态常量。
静态类中的静态常量属性应该所有字母大写。例如:
Ext.MessageBox.YES = "Yes"
Ext.MessageBox.NO = "No"
MyCompany.alien.Math.PI = "4.13"
声明
你可以仅调用一个方法就可以创建类:Ext.define。它的基本语法如下:
Ext.define(className, members,onClassCreated);
className:类名
members是一个的对象,该对象由键值对集合组成类成员。
onClassCreated是一个可选的回调函数。当定义的类的所有依赖准备好,并且该类本身已经完全被创建,这个函数就会被调用。由于类创建是异步的,这个回调函数在很多情况下非常有用。这些将在本章节第四部分讨论。
示例:
Ext.define('My.sample.Person', {
name: 'Unknown',
constructor: function(name) {
if (name) {
this.name = name;
}
},
eat: function(foodType) {
alert(this.name + " is eating: " + foodType);
}
});
var bob = Ext.create('My.sample.Person', 'Bob');
bob.eat("Salad"); // alert("Bob is eating: Salad");
注释:我们使用Ext.create()方法创建了一个My.sample.Person类的新实例。我们也可以使用new关键字(new My.sample.Person())。然而,我们还是推荐养成使用Ext.create()的使用习惯,因为它能让你有动态加载的优点。
更多关于动态加载的信息,请参看入门指南章节(http://docs.sencha.com/extjs/6.2.1/guides/getting_started/getting_started.html)。
配置
还有一个专用的config属性,它会在类创建之前被强大的Ext.class预处理器处理。它有如下特点:
* 配置项与其它类成员相比,是完全密封的。
* 如果每个配置属性的Getter与Setter方法没有定义,它们会在类的创建过程中在类的原型中自动生成。
* 自动生成的setter方法在设置值之前会调用apply方法(如果它在类中被定义)。如果你想在设置值之前需要运行自定义的逻辑代码,你可以为一个配置属性项重写并覆盖apply方法。如果你的apply方法没有返回值,setter方法将不会成功设置值。当不同的值被设置时,update方法(如果定义了的话)也会被调用。apply和update方法都会传入两个参数,一个是新值,一个是旧值。
使用configs属性的Ext类,你不需要手动调用initConfig()方法。然而 ,如果你的类是继承自Ext.Base,initConfig()方法仍然需要调用。
请看如下的配置示例。Ext.define('My.own.Window', {
extend: 'Ext.Component',
/** @readonly */
isWindow: true,
config: {
title: 'Title Here',
bottomBar: {
height: 50,
resizable: false
}
},
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) {
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 });
alert(myWindow.getBottomBar().getHeight()); // alerts 100
静态成员
静态成员可以使用statics配置项来定义
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
}
});
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"
/** A child component to complete the example. */
Ext.define('My.own.WindowBottomBar', {
config: {
height: undefined,
resizable: true
}
});
错误处理和调试
Ext JS有很多有用的特性可以帮助你调试和进行错误处理。
* 你可以使用Ext.getDisplayName()来得到任何方法的显示名称。在有类名和方法名声明的类中,对处理抛出异常错误特别有用。
throw new Error('['+ Ext.getDisplayName(arguments.callee)+'] Some message here');
* 在任何使用Ext.define()定义的类的方法中,当一个错误被抛出时,如果你使用的浏览器是基本WebKit的(Chrome或者Safari),你可以在调用堆栈中看到类名和方法名。例如,如下是在Chrome浏览器中看到的样子: