文档帮助

术语、图标和标签

许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 别名(如果类扩展自 Ext.Component,则为 xtype)。别名/xtype 列在适用类的类名旁边,以供快速参考。

访问级别

框架类或其成员可以指定为 privateprotected。否则,类/成员为 publicPublicprotectedprivate 是访问描述符,用于传达应如何以及何时使用类或类成员。

成员类型

成员语法

下面是一个示例类成员,我们可以对其进行剖析,以展示类成员的语法(在本例中是从 Ext.button.Button 类查看的 lookupComponent 方法)。

lookupComponent ( item ) : Ext.Component
protected(受保护)

当原始配置对象添加到此容器时调用,无论是在 items 配置初始化期间,还是在添加新项目 added) 或 {@link #insert 插入 时。

此方法将传递的对象转换为实例化的子组件。

当需要对子组件创建应用特殊处理时,可以在子类中覆盖此方法。

参数

item :  Object

要添加的配置对象。

返回值
Ext.Component

要添加的组件。

让我们看一下成员行的每个部分

成员标志

API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。

类图标

- 表示框架类

- 单例框架类。*有关更多信息,请参阅单例标志

- 组件类型框架类(Ext JS 框架中扩展自 Ext.Component 的任何类)

- 表示类、成员或指南在当前查看的版本中是新增的

成员图标

- 表示类型为 config 的类成员

- 表示类型为 property 的类成员

- 表示类型为 method 的类成员

- 表示类型为 event 的类成员

- 表示类型为 theme variable 的类成员

- 表示类型为 theme mixin 的类成员

- 表示类、成员或指南在当前查看的版本中是新增的

类成员快速导航菜单

在 API 文档页面上的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮都显示按类型划分的成员计数(此计数会随着过滤器的应用而更新)。单击按钮将导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。

Getter 和 Setter 方法

与类配置选项相关的 Getter 和 Setter 方法将显示在方法部分以及 API 文档和成员类型菜单的配置部分中,紧挨着它们所作用的配置下方。Getter 和 Setter 方法文档将在配置行中找到,以便于参考。

历史记录栏

您的页面历史记录保存在本地存储中,并显示在顶部标题栏正下方(使用可用的实际空间)。默认情况下,显示的唯一搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来展开显示的内容。这将显示历史记录栏中所有产品/版本的所有最近页面。

在历史记录配置菜单中,您还将看到最近访问页面的列表。结果按“当前产品/版本”和“全部”单选按钮进行过滤。单击 按钮将清除历史记录栏以及本地存储中保存的历史记录。

如果在历史记录配置菜单中选择“全部”,“在历史记录栏中显示产品详细信息”的复选框选项将启用。选中后,每个历史页面的产品/版本将与历史记录栏中的页面名称一起显示。将光标悬停在历史记录栏中的页面名称上也会将产品/版本显示为工具提示。

搜索和过滤器

可以使用页面顶部的搜索字段搜索 API 文档和指南。

在 API 文档页面上,还有一个过滤器输入字段,该字段使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这是通过使用页面顶部的复选框完成的。

API 类导航树底部的复选框过滤类列表以包含或排除私有类。

单击空的搜索字段将显示您最近 10 次搜索,以便快速导航。

API 文档类元数据

每个 API 文档页面(JavaScript 原始类型页面除外)都有一个菜单视图,其中包含与该类相关的元数据。此元数据视图将具有以下一项或多项

展开和折叠示例及类成员

可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。

类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标或全局使用右上角的展开/折叠全部切换按钮来展开和折叠成员。

桌面 -vs- 移动视图

在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别在于

查看类源代码

可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。

Ext JS 7.8.0


顶部

基本 OOP / 基于类的编程概念

JavaScript 是一种无类、基于原型的语言,其最强大的功能之一是灵活性。也就是说,基于类的编程 可以说是最流行的面向对象编程 (OOP) 模型。这种风格通常强调强类型、封装和标准编码约定。

JavaScript 的灵活性带来了不可预测性的代价。如果没有统一的结构,JavaScript 代码可能难以理解、维护和重用。另一方面,基于类的代码更有可能在一段时间内变得可预测、可扩展和可伸缩。

幸运的是,Ext JS 的类系统为您提供了两全其美的优势。您可以使用 JavaScript 的灵活性获得基于类的编程的灵活、可扩展和可伸缩的实现。

本指南旨在帮助任何想要学习或回顾 Ext JS 的 OOP 和基于类的编程概念的开发人员。我们将涵盖以下主题

  • 类和实例

  • 继承(多态性)

  • 封装

类和实例

清楚地区分类和实例非常重要。简而言之,类是一个概念的蓝图,而实例是蓝图的实现。让我们看一些例子

  • “建筑”是一个类,而帝国大厦是“建筑”的一个实例。

  • “狗”是一个类,而莱西是“狗”的一个实例。

  • “计算机”是一个类,而您正在使用的计算机是“计算机”的一个实例。

image alt text

类定义了其实例的基本结构、属性和行为。例如,使用上面描述的相同类

  • “建筑”的所有实例都有给定的楼层数(结构)、地址和营业时间(属性)。此外,假设这些是“智能建筑”,它们可以根据需要关闭和锁定其主入口(行为)。

  • “狗”的所有实例都有 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(' ')
 );

Apply

我们可以使用 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 />'));

Config 块

为了防止直接读取/写入对象的属性,我们将使用 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.Base 类

在 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 支持客户 访问)。

Ext JS 7.8.0