许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 别名
(如果类扩展自 Ext.Component,则为 xtype
)。别名/xtype 列在适用类的类名旁边,以供快速参考。
框架类或其成员可以指定为 private
或 protected
。否则,类/成员为 public
。Public
、protected
和 private
是访问描述符,用于传达应如何以及何时使用类或类成员。
Public(公共) 类和类成员可供任何其他类或应用程序代码使用,并且在主要产品版本中可以作为稳定和持久的内容依赖。公共类和成员可以通过子类安全地扩展。
Protected(受保护) 类成员是稳定的 public
成员,旨在由拥有类或其子类使用。受保护的成员可以通过子类安全地扩展。
Private(私有) 类和类成员在框架内部使用,不供应用程序开发人员使用。私有类和成员可能会随时更改或从框架中省略,恕不另行通知,并且不应在应用程序逻辑中依赖。
static(静态)
标签。*请参阅下面的静态。下面是一个示例类成员,我们可以对其进行剖析,以展示类成员的语法(在本例中是从 Ext.button.Button 类查看的 lookupComponent 方法)。
让我们看一下成员行的每个部分
lookupComponent
)( item )
)Ext.Component
)。对于不返回除 undefined
之外任何内容的方法,可以省略此项,或者可以显示为由斜杠 /
分隔的多个可能值,表示返回的内容可能取决于方法调用的结果(即,如果 get 方法调用成功,方法可能会返回 Component,如果失败,则返回 false
,这将显示为 Ext.Component/Boolean
)。PROTECTED(受保护)
- 请参阅下面的标志部分)Ext.container.Container
)。如果成员源自当前类,则源类将显示为蓝色链接;如果成员从祖先类或混合类继承,则显示为灰色链接。view source(查看源代码)
)item : Object
)。undefined
之外的值,“返回值”部分将注明返回的类或对象类型以及描述(示例中的 Ext.Component
)Available since 3.4.0(始于 3.4.0 版本)
- 示例中未显示),紧随成员描述之后Defaults to: false(默认为:false)
)API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。
classInstance.method1().method2().etc();
false
,则标记为可阻止的事件将不会触发- 表示框架类
- 单例框架类。*有关更多信息,请参阅单例标志
- 组件类型框架类(Ext JS 框架中扩展自 Ext.Component 的任何类)
- 表示类、成员或指南在当前查看的版本中是新增的
- 表示类型为 config
的类成员
- 表示类型为 property
的类成员
- 表示类型为 method
的类成员
- 表示类型为 event
的类成员
- 表示类型为 theme variable
的类成员
- 表示类型为 theme mixin
的类成员
- 表示类、成员或指南在当前查看的版本中是新增的
在 API 文档页面上的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮都显示按类型划分的成员计数(此计数会随着过滤器的应用而更新)。单击按钮将导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。
与类配置选项相关的 Getter 和 Setter 方法将显示在方法部分以及 API 文档和成员类型菜单的配置部分中,紧挨着它们所作用的配置下方。Getter 和 Setter 方法文档将在配置行中找到,以便于参考。
您的页面历史记录保存在本地存储中,并显示在顶部标题栏正下方(使用可用的实际空间)。默认情况下,显示的唯一搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来展开显示的内容。这将显示历史记录栏中所有产品/版本的所有最近页面。
在历史记录配置菜单中,您还将看到最近访问页面的列表。结果按“当前产品/版本”和“全部”单选按钮进行过滤。单击 按钮将清除历史记录栏以及本地存储中保存的历史记录。
如果在历史记录配置菜单中选择“全部”,“在历史记录栏中显示产品详细信息”的复选框选项将启用。选中后,每个历史页面的产品/版本将与历史记录栏中的页面名称一起显示。将光标悬停在历史记录栏中的页面名称上也会将产品/版本显示为工具提示。
可以使用页面顶部的搜索字段搜索 API 文档和指南。
在 API 文档页面上,还有一个过滤器输入字段,该字段使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这是通过使用页面顶部的复选框完成的。
API 类导航树底部的复选框过滤类列表以包含或排除私有类。
单击空的搜索字段将显示您最近 10 次搜索,以便快速导航。
每个 API 文档页面(JavaScript 原始类型页面除外)都有一个菜单视图,其中包含与该类相关的元数据。此元数据视图将具有以下一项或多项
Ext.button.Button
类具有备用类名称 Ext.Button
)。备用类名称通常是为了向后兼容性而维护的。可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。
类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标或全局使用右上角的展开/折叠全部切换按钮来展开和折叠成员。
在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别在于
可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。
JavaScript 是一种无类、基于原型的语言,其最强大的功能之一是灵活性。也就是说,基于类的编程 可以说是最流行的面向对象编程 (OOP) 模型。这种风格通常强调强类型、封装和标准编码约定。
JavaScript 的灵活性带来了不可预测性的代价。如果没有统一的结构,JavaScript 代码可能难以理解、维护和重用。另一方面,基于类的代码更有可能在一段时间内变得可预测、可扩展和可伸缩。
幸运的是,Ext JS 的类系统为您提供了两全其美的优势。您可以使用 JavaScript 的灵活性获得基于类的编程的灵活、可扩展和可伸缩的实现。
本指南旨在帮助任何想要学习或回顾 Ext JS 的 OOP 和基于类的编程概念的开发人员。我们将涵盖以下主题
类和实例
继承(多态性)
封装
清楚地区分类和实例非常重要。简而言之,类是一个概念的蓝图,而实例是蓝图的实现。让我们看一些例子
“建筑”是一个类,而帝国大厦是“建筑”的一个实例。
“狗”是一个类,而莱西是“狗”的一个实例。
“计算机”是一个类,而您正在使用的计算机是“计算机”的一个实例。
类定义了其实例的基本结构、属性和行为。例如,使用上面描述的相同类
“建筑”的所有实例都有给定的楼层数(结构)、地址和营业时间(属性)。此外,假设这些是“智能建筑”,它们可以根据需要关闭和锁定其主入口(行为)。
“狗”的所有实例都有 4 条腿和一条尾巴(结构)。它们也有一个名字(属性)并且能够吠叫(行为)。
“计算机”的所有实例都有 CPU 和某种形式的内存(结构)、型号名称(属性)并且能够打开和关闭(行为)。
让我们定义一个类,它将作为我们探索基于类的编程概念的基础。我们将从“Square(正方形)”类开始,该类表示正方形以及用于计算其面积的简单方法。
您可以使用以下语法定义 Square 类
// Define a new class named: 'Square'
Ext.define('Square', {
// The side property represents the length of the side
// It has a default value of 0
side: 0,
// It also has a method to calculate its area
getArea: function() {
// We access the 'side' property to calculate area
return this.side * this.side;
}
});
// We use Ext.create to create an instance of our Square class
var sq = Ext.create('Square');
// The value of the 'side' property
// This is not the best way to do this, which we'll discuss below
sq.side = 4;
// Display a message and show the result of this calculation
Ext.Msg.alert('Message', 'The area is: ' + sq.getArea());
这是使用 Ext JS 的类的基本实现。虽然它确实满足了我们表示正方形并提供计算其面积的方法的目标,但它不是理想的或良好的实践。
让我们通过利用构造函数来改进此示例。构造函数是一个特殊的函数,在类实例化时调用。首先,让我们更改设置 Square 边值的方式。通过利用构造函数,我们可以从上面的示例中删除“丑陋”的行。
Ext.define('Square', {
side: 0,
// This is a special function that gets called
// when the object is instantiated
constructor: function (side) {
// It receives the side as a parameter
// If defined, it is set as the square's side value
if (side) {
this.side = side;
}
},
getArea: function () {
return this.side * this.side;
}
});
// Thanks to the constructor, we can pass 'side's' value
// as an argument of Ext.create
// This is a slightly more elegant approach.
var sq = Ext.create('Square', 4);
// The passed value is assigned to the square's side property
// Display a message to make sure everything is working
Ext.Msg.alert('Message', 'The area is: ' + sq.getArea());
如果想要将两个或多个属性值传递给构造函数,可以使用对象字面量执行此操作,如下所示
Ext.define('Square', {
side: 0,
// We have added two more configs
color: 'red',
border: true,
// Pass a config object, which contains 'side's' value
constructor: function(config) {
// Once again, this is not yet the best syntax
// We'll get to that in the next example
if (config.side) {
this.side = config.side;
}
if (config.color) {
this.color = config.color;
}
// border is a boolean so we can skip the if block
this.border = config.border;
},
getArea: function() {
return this.side * this.side;
}
});
// We pass an object containing properties/values
var sq = Ext.create('Square', {
side: 4,
border: false
});
// Now display a message that uses the other two properties
// Note that we're accessing them directly (i.e.: sq.color)
// This will change in the next section
Ext.Msg.alert('Message',
['The area of the',sq.color,'square',
(sq.border?'with a border':''),'is:',
sq.getArea()].join(' ')
);
我们可以使用 Ext.apply
进一步清理构造函数。Ext.apply 将配置的所有属性复制到指定的对象。
注意:构造函数将在继承部分再次更改。
Ext.define('Square', {
side: 0,
color: 'red',
border: true,
constructor: function(config) {
// Use Ext.apply to not set each property manually
// We'll change this again in the "Inheritance" section
Ext.apply(this,config);
},
getArea: function() {
return this.side * this.side;
}
});
var sq = Ext.create('Square', {
side: 4,
border: false
});
Ext.Msg.alert('Message',
['The area of the',sq.color,'square',
(sq.border?'with a border':''),'is:',
sq.getArea()].join(' ')
);
让我们添加 Circle 和 Rectangle 类,以便展示与 Square 示例的一些细微偏差。
Ext.define('Square', {
side: 0,
color: 'red',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
return this.side * this.side;
}
});
Ext.define('Rectangle', {
//Instead of side, a rectangle cares about base and height
base: 0,
height: 0,
color: 'green',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
// The formula is different
return this.base * this.height;
}
});
Ext.define('Circle', {
// A circle has no sides, but radius
radius: 0,
color: 'blue',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
// Just for this example, fix the precision of PI to 2
return Math.PI.toFixed(2) * Math.pow(this.radius, 2);
}
});
var square = Ext.create('Square', {
side: 4,
border: false
}),
rectangle = Ext.create('Rectangle', {
base: 4,
height: 3
}),
circle = Ext.create('Circle', {
radius: 3
});
// This message will now show a line for each object
Ext.Msg.alert('Message', [
['The area of the', square.color, 'square',
(square.border ? 'with a border' : ''), 'is:',
square.getArea()].join(' '),
['The area of the', rectangle.color, 'rectangle',
(rectangle.border ? 'with a border' : ''), 'is:',
rectangle.getArea()].join(' '),
['The area of the', circle.color, 'circle',
(circle.border ? 'with a border' : ''), 'is:',
circle.getArea()].join(' ')
].join('<br />'));
在深入探讨继承的概念之前,让我们回顾一下以下示例。如下所示,我们在 Square 类中添加了一个附加方法,并更改了生成测试消息的方式
Ext.define('Square', {
side: 0,
color: 'red',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
return this.side * this.side;
},
// This function will return the name of this shape
getShapeName: function () {
return 'square';
}
});
//This function generates a sentence to display in the test dialog
function generateTestSentence(shape) {
return ['The area of the', shape.color, shape.getShapeName(),
(shape.border ? 'with a border' : ''),
'is:', shape.getArea()].join(' ');
}
var square = Ext.create('Square', {
side: 4,
border: false
});
Ext.Msg.alert('Message', generateTestSentence(square));
在下一个示例中,我们将对 Rectangle 和 Circle 类应用相同的更改
Ext.define('Square', {
side: 0,
color: 'red',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
return this.side * this.side;
},
getShapeName: function () {
return 'square';
}
});
Ext.define('Rectangle', {
base: 0,
height: 0,
color: 'green',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
return this.base * this.height;
},
getShapeName: function () {
return 'rectangle';
}
});
Ext.define('Circle', {
radius: 0,
color: 'blue',
border: true,
constructor: function(config) {
Ext.apply(this, config);
},
getArea: function() {
return Math.PI.toFixed(2) * Math.pow(this.radius, 2);
},
getShapeName: function () {
return 'circle';
}
});
// Generates a sentence that will be displayed in the test dialog
function generateTestSentence(shape) {
return ['The area of the', shape.color, shape.getShapeName(),
(shape.border ? 'with a border' : ''), 'is:',
shape.getArea()].join(' ');
}
var square = Ext.create('Square', {
side: 4,
border: false
}),
rectangle = Ext.create('Rectangle', {
base: 4,
height: 3
}),
circle = Ext.create('Circle', {
radius: 3
});
Ext.Msg.alert('Message', [
generateTestSentence(square),
generateTestSentence(rectangle),
generateTestSentence(circle)
].join('<br />'));
如果仔细回顾上面的示例,您可能会注意到很多重复。这会使您的代码难以维护且容易出错。继承的概念帮助我们整合重复的代码,并使其更易于理解和维护。
通过应用继承的概念,我们可以通过为子类提供父类的属性来简化和减少重复代码
// The shape class contains common code to each shape class
// This allows the passing of properties on child classes
Ext.define('Shape', {
// Let's define common properties here and set default values
color: 'gray',
border: true,
// Let's add a shapeName property and a method to return it
// This replaces unique getShapeName methods on each class
shapeName: 'shape',
constructor: function (config) {
Ext.apply(this, config);
},
getShapeName: function () {
return this.shapeName;
}
});
Ext.define('Square', {
// Square extends from Shape so it gains properties
// defined on itself and its parent class
extend: 'Shape',
// These properties will 'override' parent class properties
side: 0,
color: 'red',
shapeName: 'square',
getArea: function() {
return this.side * this.side;
}
});
//This function generates a sentence to display in the test dialog
function generateTestSentence(shape) {
return ['The area of the', shape.color, shape.getShapeName(),
(shape.border ? 'with a border' : ''),
'is:', shape.getArea()].join(' ');
}
var square = Ext.create('Square', {
side: 4
});
// Since Square extends from Shape, this example will work since
// all other properties are still defined, but now by 'Shape'
Ext.Msg.alert('Message',
[ generateTestSentence(square) ].join('<br />'));
我们甚至可以将 generateTestSentence()
方法移动到 Shape 类
Ext.define('Shape', {
color: 'gray',
border: true,
shapeName: 'shape',
constructor: function (config) {
Ext.apply(this, config);
},
getShapeName: function () {
return this.shapeName;
},
// This function will generate the test sentence for this shape,
// so no need to pass it as an argument
getTestSentence: function () {
return ['The area of the', this.color, this.getShapeName(),
(this.border ? 'with a border' : ''),
'is:', this.getArea()].join(' ');
}
});
Ext.define('Square', {
extend: 'Shape',
side: 0,
color: 'red',
shapeName: 'square',
getArea: function() {
return this.side * this.side;
}
});
var square = Ext.create('Square', {
side: 4
});
// The generateTestSentence function doesn't exist anymore
// so use the one that comes with the shape
Ext.Msg.alert('Message',
[ square.getTestSentence() ].join('<br />'));
如您所见,如果同时设置了子类和父类上的属性,则子类上的属性将覆盖父类上的属性。例如,Shape 的 shapeName
是“shape”。但是,由于 shapeName
也在 Square 类上设置,因此它会覆盖父类的值。如果子类没有设置属性,它将从父类继承该属性。
在前面的示例中,您可能会注意到我们通过直接调用来访问实例属性。例如,通过访问“square.color”来获取正方形的颜色。您也可以直接设置该值
Ext.define('Shape', {
color: 'gray',
border: true,
shapeName: 'shape',
constructor: function (config) {
Ext.apply(this, config);
},
getShapeName: function () {
return this.shapeName;
},
getTestSentence: function () {
return ['The area of the', this.color, this.getShapeName(),
(this.border ? 'with a border' : ''),
'is:', this.getArea()].join(' ');
}
});
Ext.define('Square', {
extend: 'Shape',
side: 0,
color: 'red',
shapeName: 'square',
getArea: function() {
return this.side * this.side;
}
});
var square = Ext.create('Square', {
side: 4
});
// Set the value of 'side' to 5 instead of the initial 4
// While not bad, this is something that should be avoided
square.side = 5;
// Set the value of 'side' to a string instead of a number
// String is not a valid value. This is an example of why
// direct access to the properties should be avoided.
// Open access is prone to error.
square.side = 'five';
// The area will be reported as NaN
Ext.Msg.alert('Message',
[ square.getTestSentence() ].join('<br />'));
为了防止直接读取/写入对象的属性,我们将使用 Ext JS 的 config
块。这将自动限制对对象属性的访问,以便只能使用访问器方法设置和检索它们。
访问器方法是为类配置块中的任何内容自动生成的 getter 和 setter。例如,如果在配置块中具有 shapeName
,则默认情况下会获得 setShapeName()
和 getShapeName()
。
注意:配置块应仅包含其类独有的新配置。您不应包含父类的配置块中已定义的配置。
Ext.define('Shape', {
// All properties inside the config block have
// their accessor methods automatically generated
config: {
color: 'gray', // creates getColor|setColor
border: true, // creates getBorder|setBorder
shapeName: 'shape' // creates getShapeName|setShapeName
},
constructor: function (config) {
Ext.apply(this, config);
// Initialize the config block for this class
// This auto-generates the accessor methods
// More information on this in the next section
this.initConfig(config);
},
// We have removed the getShapeName method
// It's auto-generated since shapeName is in the config block
// Now we can use the accessor methods instead
// of accessing the properties directly
getTestSentence: function () {
return ['The area of the', this.getColor(),
this.getShapeName(),
(this.getBorder() ? 'with a border' : ''), 'is:',
this.getArea()].join(' ');
}
});
Ext.define('Square', {
extend: 'Shape',
// In a child class, the config block should only
// contain new configs particular for this class
config: {
side: 0 // getSide and setSide are now available
},
// Parent class properties are defined outside the config block
color: 'red',
shapeName: 'square',
getArea: function() {
// We're using the accessor methods of the 'side' config
return this.getSide() * this.getSide();
}
});
var square = Ext.create('Square', {
side: 4
});
// The following line won't modify the value of 'side' anymore
square.side = 'five';
// To modify it instead, we'll use the setSide method:
square.setSide(5);
// The area will be reported as 25
Ext.Msg.alert('Message',
[ square.getTestSentence() ].join('<br />'));
在 Ext JS 中,除非明确指定,否则所有类都是公共基类的子类。此基类是 Ext.Base
。
就像我们的 Square 类扩展自 Shape 一样,Shape 自动扩展自 Ext.Base。
基于此逻辑,以下代码
Ext.define('Shape', {
// Properties and methods here
});
实际上等同于此代码
Ext.define('Shape', {
extend: 'Ext.Base'
// Properties and methods here
});
这就是为什么我们可以在 Shape 的构造函数中使用 this.initConfig(config);。initConfig()
是 Ext.Base 的方法,并且被从它扩展的任何内容继承。initConfig()
初始化其类的配置块并自动生成访问器方法。
封装的主要目标是保护对象免受不需要的和/或无效的属性修改。这些修改将不可避免地导致错误。
例如,当使用 config
块来避免直接属性修改时,目前没有任何措施可以阻止将无效值传递给访问器方法。也就是说,没有任何措施可以阻止我们调用 square.setSide('five'),这将导致错误,因为 side 期望一个数字。
让我们通过使用 apply 方法来防止这种情况。Apply 是一种模板方法,允许您在修改之前测试建议的值。此方法将配置的所有属性复制到指定的对象。
由于 'side' 是通过 'config' 块定义的属性,我们可以利用此模板方法在实际修改值之前采取操作,例如检查 'side' 的新值是否确实是数字。
Ext.define('Shape', {
config: {
color: 'gray',
border: true,
shapeName: 'shape'
},
constructor: function (config) {
Ext.apply(this, config);
this.initConfig(config);
},
getTestSentence: function () {
return ['The area of the', this.getColor(),
this.getShapeName(),
(this.getBorder() ? 'with a border' : ''),
'is:', this.getArea()].join(' ');
}
});
Ext.define('Square', {
extend: 'Shape',
config: {
side: 0
},
color: 'red',
shapeName: 'square',
getArea: function() {
return this.getSide() * this.getSide();
},
// 'side' is a property defined through the 'config' block,
// We can use this method before the value is modified
// For instance, checking that 'side' is a number
applySide: function (newValue, oldValue) {
return (Ext.isNumber(newValue)? newValue : oldValue);
}
});
var square = Ext.create('Square', {
side: 4
});
// The following line won't modify the value of 'side'
square.setSide('five');
// The area will be reported as 16
Ext.Msg.alert('Message',
[ square.getTestSentence() ].join('<br />'));
我们希望本指南阐明了 Ext JS 中 OOP 和基于类的编程的基本概念。请务必查看 类系统指南,以获取有关如何在制作自己的应用程序时利用 Ext JS 类系统的更多信息。与往常一样,如果您对指南内容有疑问,请务必在 社区论坛 上提问,或通过 支持门户 提交支持票证(仅限 Sencha 支持客户 访问)。