前言

我们知道,JavaScript中没有真正的类,它是一种面向原型的语言 。这种语言一个强大的特性就是灵活,实现一个功能可以有很多不同的方式,用不同的编码风格和技巧。但随之也带来了代码的不可预测和难以维护等问题。代码量很大时,由于JavaScript 没有统一的结构,代码变得很难理解和阅读,不方便维护和重用。

而像C#这种基于类的面向对象语言,它是强类型的,具有封装、继承和多态的OOP基本特征,而且都有标准的编码约定。它通过强制开发者遵循一系列的原则,让编写的代码更具有可预测性和可扩展性等优点。然而,这种语言却不能像JavaScript语言一样可以灵活使用。
这两种语言各自都有他们的缺点和优点,能不能集合两者的优点而摒弃它们的缺点呢?在 Ext JS 4 身上似乎能看到这种结合体的影子,让我们来一看究竟。

命名规则

Ext JS 中命名空间、包和类的层次关系如下图所示:

基于类、命名空间和文件名的一致的命名规范则,可以帮助我们良好地组织代码,使其有清晰的层次结构,并且容易阅读。Ext JS 框架中的所有类都基于这种命名规则,同时官方建议用户自定义的类也遵照这样的规则。这个规则如何约定,具体如下:

1)类的命名约定

类名只可含字母和数字,数字虽然允许,但不推荐类的名字中含有数字,除非类名本身是一个专业术语。不要使用下划线(_)、连字符(-)等其他非字母数字的字符。如:
MyCompany.util.Toolbar1 类名含数字,不推荐;
MyCompany.useful_util.Debug_Toolbar 含下划线,不推荐;
MyCompany.util.Base64 Base64 合理。

类应该用包进行分组,至少有一个父层,且顶层一定是命名空间,如:
MyCompany.util.Toolbar 合理;
MyCompany.Application 合理。

命名空间和类的命名遵循CamelCased风格,包括简写词,其它都应小写,如:
MyCompany.form.action.AutoLoad 合理;
Ext.data.JsonProxy 合理;
Ext.data.JSONProxy 不推荐。

另外,为避免和Ext JS框架内部类发生冲突,用户自定义类的顶层最好不要用Ext。

2)代码文件

代码文件的存放目录应该和类的全名一一对应,最好是一个类存放一个文件,如:
Ext.util.Observable 应存放在:项目源码目录/Ext/util/Observable.js ;
Ext.form.action.Submit 应存放在:项目源码目录/Ext/form/action/Submit.js;
MyCompany.chart.axis.Numeric 应存放在:项目源码目录/MyCompany/chart/axis/Numeric.js。

3)方法、变量和属性的命名约定

和类的命名规则类似,方法、变量和属性的命名只可包含字母和数字,数字是允许的,但不推荐,除非专业术语。
方法和变量都应遵循camelCased风格,包括简写。
除了当属性是常量时需全部大写,其他情况完全和方法、变量的命名相同。
如:
合理的方法命名:encodeUsingMd5(), getHtml(), getJsonResponse(), parseXmlContent() ;
合理的变量命名:var isGoodName, var base64Encoder, var xmlReader, var httpServer 。
合理的属性命名:Ext.MessageBox.YES = "Yes", MyCompany.alien.Math.PI = "4.13", MyCompany.Person.Name="Jim" 。

类的声明/创建

在以前的 Ext JS 版本中用 Ext.extend 来创建一个类,在我另一篇博文ExtJS框架基础:事件模型及其常用功能中有Ext.extend的例子。在以前的版本中为了遵循命名规范,我们可能这样来创建一个类:

Ext.ns('My.cool');//Ext.ns是Ext.namespace 的简写.
My.cool.Window = Ext.extend(Ext.Window, { ... });

老方法中,My.cool.Window必需依赖于另一个已经存在的类创建。但在Ext JS 4中,创建一个类,只需一个方法,且可以不依赖另一个类创建,语法如下:

Ext.define(className, members, onClassCreated);

其中:className是类的全名;members是大括号括起来的一些键值对;onClassCreated是一个可选回调函数,当该类创建完毕时调用。来看个实际的例子:

Ext.onReady(function () {
Ext.define('My.sample.Person', {
name: 'Unknown',
constructor: function (name) {
if (name) {
this.name = name;
}
},
eat: function (foodType) {
alert(this.name + " is eating: " + foodType);
}
}, function () {
alert("Person's instance has created!");
}); var jim = Ext.create('My.sample.Person', 'Jim');//alert("Person's instance has created!")
jim.eat("Salad"); // alert("Jim is eating: Salad")
});

我们一般用Ext.create()来创建一个类的实例,也可以通过new关键字来创建,但推荐使用Ext.create(),Ext.create()能根据依赖关系动态加载创建新类所需要的类。

Ext JS 4中的OOP特性

我们知道,继承、封装和多态是OOP的三个基本特性。为让大家能用大家熟悉的OOP思想编程,Ext JS 4也很好的实现了继承、封装和多态。
我们先来看看Ext JS 4 中的继承,在上面的创建My.sample.Person类的例子中,我们再新建一个 My.sample.Student 类,并让它继承 My.sample.Person 类,如下:

Ext.define('My.sample.Student', {
//继承父类:My.sample.Person
extend: 'My.sample.Person',
//子类的构造器,如果子类有config配置项,则这项必需有。
constructor: function (config) {
this.initConfig(config);
this.callParent([config]);
},
major: function (speciality) {
//子类继承了父类的所有属性,比如下面的name。
alert(this.name + " majored in: " + speciality);
return this;
}
}); //创建子类实例,父类的构造器先于子类构造器被调用。
var lily = Ext.create('My.sample.Student', 'Lily');
//调用父类中的eat方法
lily.eat('rice'); //alert("Lily is eating: rice")
//调用自己的major方法
lily.major('Computer'); //alert("Lily majored in: Computer")

要继承一个父类,如果子类中有config配置项,则子类的constructor是必需的,不能省略。除了语法上的差别,Ext JS 4 中的继承非常类似于C#中的继承。

Ext JS 4 既然有继承,那么就肯定有OOP的另一个特性:多态。最明显的是方法的覆盖。在上面的Student例子中,如果要覆盖父类的eat方法,只需加上自己的eat配置项:

...
eat: function (foodType, tool) {
alert(this.name + " is eating " + foodType + " by " + tool+".");
},
...
//调用子类的eat方法
lily.eat('rice', 'tachyon');

在后面讲的Mixins部分,其实也是一种多态。

封装的特性就没什么好说的了,比如,Ext使用Ext.lib.Event、Ext.EventManager和Ext.EventObject对原生浏览器事件进行了封装,最后给我们用的是一套统一的跨浏览器的通用事件接口。使用的时候我们无需关心其内部的实现。

相信对于学过OOP语言的朋友来说,这些特性应该非常好理解,大家在使用Ext JS 4 编程的时候再好好体会吧。下面让我们继续深入对 Ext JS 4 类的学习。

读写器的实现 - Config

Ext JS 4中的读写器意义上类似于C#属性的读写器,只是语法上大不相同。Ext JS 4可通过config配置项实现对私有成员的封装和对成员的读写控制。看下面的例子:

Ext.onReady(function () {
Ext.define('My.own.Window', {
isWindow: true,//只读属性。
config: {
//config中的配置项都可通过 getXxx()来读取值。
title: 'Title Here'
},
//构造器,初始化config
  constructor: function (config) {
this.initConfig(config);
},
//applyTitle,Title对应config中的配置项title,写值时内部自动调用。
applyTitle: function (title) {
//这里可对写值做一些控制
if (!Ext.isString(title) || title.length === 0) {
alert('Error: Title must be a valid non-empty string');
}
else {
//在这里可以对写入的值进行加工。
return title;
}
}
}); var myWindow = Ext.create('My.own.Window', {
title: 'Hello World'
}); 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"
});

注意,读、写器内部是自动执行的,所以要实现自动读写器一定要注意命名。如,config中的title配置项,它的写值属性一定是applyTitle,读值和取值方法分别是getTitle()和setTitle()。

静态成员 - Statics

我们知道,在C#中,我们可以直接通过类来访问类中的静态成员,而不需要实例化。在Ext JS 4 中也可以做到。如下代码所示:

Ext.onReady(function () {
Ext.define('Computer', {
statics: {
instanceCount: 0,
factory: function (brand) {
//'this'在静态方法中代表Computer类本身
return new this({ brand: brand });
}
},
config: {
brand: null
},
constructor: function (config) {
this.initConfig(config);
//'self'属性代表Computer类本身
this.self.instanceCount++;
}
}); //调用静态函数创建两个实例。
var dellComputer = Computer.factory('Dell');
var appleComputer = Computer.factory('Mac'); //自动读取config配置项的brand属性。
alert(appleComputer.getBrand()); // Alerts "Mac"
//调用静态属性。
alert(Computer.instanceCount); // Alerts "2"
});

他山之石 - Mixins

为了达到功能的复用,Ext JS 4中出现了混用的概念,即把某个类的功能"混"(Mixed)到另一个类之中。比如,我们要创建一个Musician类,让他具有唱歌和弹吉他的功能,则可以这样来实现:

Ext.onReady(function () {
Ext.define('My.sample.Person', {
name: 'Unknown',
constructor: function (name) {
if (name) {
this.name = name;
}
},
eat: function (foodType) {
alert(this.name + " is eating: " + foodType);
}
}); // 创建会唱歌的类
Ext.define('My.sample.CanSing', {
sing: function (songName) {
alert("I'm singing " + songName);
}
}); // 创建会弹吉他的类
Ext.define('My.sample.CanPlayGuitar', {
playGuitar: function () {
alert("I'm playing guitar");
}
}); Ext.define('My.sample.Musician', {
extend: 'My.sample.Person',
mixins: {
//混入其他类中的功能
canSing: 'My.sample.CanSing',
canPlayGuitar: 'My.sample.CanPlayGuitar'
}
}); var lily = Ext.create('My.sample.Musician', "Lily");
//调用其他类中的方法
lily.sing("November Rain"); // alerts "I'm singing November Rain"
lily.playGuitar(); // alerts "I'm playing guitar"
});

这里,我们还可以对My.sample.CanSing中的sing方法进行重写实现Musician自己的sing方法,只需要在Musician类中加上自己的sing配置项:

...
sing: function () {
alert("Attention!");
// this.mixins是指向所有mixins配置项的引用
return this.mixins.canSing.sing.apply(this, arguments);
}
...

注意,这里为了演示我把所有类的代码放在一起写。在实际项目开发时,遵照Ext的命名约定,每个类应该根据全名放在特定的文件夹和文件中,如上面的My.sample.Person类存放的文件应该是My/sample/Person.js,My.sample.CanSing类存放的文件应该是My/sample/CanSing.js ,而Ext.onReady(...)中的内容应该放在和实际的业务页面名称相同的js文件中。

结束语

Ext JS 4 是一个含有300多个类的框架,它具有快速开发、可扩展,容易维护等优点。Ext JS 4 框架让开发者用他们熟悉面向对象方法进行开发。除了本文介绍的,Ext JS 4还有很多吸引人的功能,我将会在后面的博文中进行介绍。

参考:
http://docs.sencha.com/extjs/4.0.7/#!/guide/class_system

Ext JS 4 的类系统的更多相关文章

  1. Ext JS 6学习文档–第2章–核心概念

    核心概念 在下一章我们会构建一个示例项目,而在这之前,你需要学习一些在 Ext JS 中的核心概念,这有助于你更容易理解示例项目.这一章我们将学习以下知识点: 类系统,创建和扩展类 事件 Ext JS ...

  2. 【翻译】对于Ext JS 5,你准备好了吗?

    原文:Are You Ready for Ext JS 5? Ext JS 5:准备升级 对于Ext JS 5加入Sencha的大家庭,我们感到非常高兴!作为一个主要版本,在Ext JS 5引入了一堆 ...

  3. 【翻译】Ext JS——高效的编码风格指南

    原文:ExtJS - Efficient coding style guide 作者:Raja 切勿使用"new"关键字:在Ext JS中,使用"new"关键字 ...

  4. 【翻译】探究Ext JS 5和Sencha Touch的布局系统

    原文:Exploring the Layout System in Ext JS 5 and Sencha Touch 布局系统是Sencha框架中最强大和最有特色的一个部分. 布局要处理应用程序中每 ...

  5. 谈谈Ext JS的组件——组件基类:Ext.Component

    概述 Ext.Component是所有Ext组件的基类,这在Ext.Component的API中第一句话就提到了.然后第二段说明了它包含的基本功能:隐藏/显示.启用/禁用以及尺寸控制等.除了以上这些基 ...

  6. Ext JS学习第六天 Ext自定义类(一)

    此文来记录学习笔记 •我们在之前的学习,已经对ExtJS有了一个初步的认识,那么如果要学好ExtJS,对于javascript是必须的,也就是说,对于理解ExtJS底层基础架构的理解也是必须的.那么我 ...

  7. Ext JS - 问答

    Ext JS - 问答 在下面你将可以找到关于Ext JS 的最常见问题的答复.如果没有找到您所需的答复,请访问 Ext JS 论坛或者提交一个支持申请. 如果你确信你的问题可以对本页有补充,请让我们 ...

  8. Ext JS 4 新特性2:配置项属性(config)之二

    Ext JS 4 新特征2:配置项属性config之二 ☞ Config(自动的setters和getters) Ext JS 4介绍了config声明方式,在Ext JS 中也有几个例子:在运行程序 ...

  9. [转]使用Sencha Ext JS 6打造通用应用程序

    原文地址:http://www.uedsc.com/using-sencha-ext-js-6-to-build-universal-apps.html 在Sencha和整个Ext JS团队的支持下, ...

随机推荐

  1. 使用linq语句进行联表查询

    假设你有一个父表(例如:汽车),其关联一个子表,例如轮子(一对多).现在你想对于所有的父表汽车,遍历所有汽车,然后打印出来所有轮子的信息.默认的做法将是: SELECT CarId FROM Cars ...

  2. EF的CodeFirst模式自动迁移(适用于开发环境)

    EF的CodeFirst模式自动迁移(适用于开发环境) 1.开启EF数据迁移功能 NuGet包管理器------>程序包管理控制台---------->Enable-Migrations ...

  3. layer层、modal模拟窗 单独测试页面

    layer_test.jsp <%@ page language="java" import="java.util.*" pageEncoding=&qu ...

  4. Dos命令讲解

    目录 一.什么是DOS 二.启动DOS的多种方法 三.DOS的内部命令与外部命令 四.系统环境变量讲解 增加Path环境变量路径 常见的系统环境变量 五.常用的运行命令 六.DOS使用技巧 设置CMD ...

  5. [php] yii debug设置

    最近在使用yii写rest api发现在所有错误信息都写在log文件里调试不是很直接 在index.php头部查入就ok了 defined('YII_ENABLE_EXCEPTION_HANDLER' ...

  6. node_01_自定义模块(先创建package.json)

    package.json必须是json格式 你必须确保所有的字符串,包括属性名,都是使用双引号而不是单引号 { "name": "163", "ver ...

  7. 玩转FusionCharts:Y轴数字形式(如去掉K)

    玩转FusionCharts:Y轴数字形式(如去掉K) 如果运行FusionCharts带的例子,你会发现FusionCharts表中的数字(通常是Y轴)会带上’k’,也就是如20000,会变成20k ...

  8. C#反射の反射详解

    C#反射の反射详解(点击跳转)C#反射の反射接口(点击跳转)C#反射反射泛型接口(点击跳转)C#反射の一个泛型反射实现的网络请求框架(点击跳转) 一.什么是反射 反射(Reflection):这是.N ...

  9. Tensorflow张量

    张量常规解释 张量(tensor)理论是数学的一个分支学科,在力学中有重要应用.张量这一术语起源于力学,它最初是用来表示弹性介质中各点应力状态的,后来张量理论发展成为力学和物理学的一个有力的数学工具. ...

  10. wifi 安卓手机

    通过wifi向服务器端发送数据 https://www.cnblogs.com/zhaoxinshanwei/p/3573813.html http://www.hx-wl.com.cn/51wifi ...