第九天(How to Use Classes in Sencha Touch)

来源:互联网 发布:剑灵多玩捏脸数据 编辑:程序博客网 时间:2024/05/03 00:48

原文地址:http://docs.sencha.com/touch/2.2.0/#!/guide/class_system

 

周五的,现在补上,周六、周日过的比工作日还累,伤不起啊


How to Use Classes in Sencha Touch

如何使用st中的类

Contents

  1. Dependencies and Dynamic Loading
  2. Naming Conventions
  3. Working with Classes in Sencha Touch 2
  4. Error Handling and Debugging
  5. Further Reading

Watch the Class System video from SenchaCon 2011

Sencha Touch uses the state-of-the-art class system developed for Ext JS 4. This class system makes it easy to create new classes in JavaScript, providing inheritance, dependency loading, mixins, powerful configuration options, and lots more.

st用Ext js4最先进的类系统开发。该类系统使得在js中创建新类变得很容易,并提供了实例化、依赖加载、混合的功能强大的配置及更多的东西。

At its simplest, a class is just an object with some functions and properties attached to it. For instance, here is a class for an animal, recording its name and a function that makes it speak:

最简单情况下,一个类只是有一些依附于它的方法和属性的对象。例如,这里有一个动物的类,记录该动物的名字并让他能够说话:

Ext.define('Animal', {    config: {        name: null    },    constructor: function(config) {        this.initConfig(config);    },    speak: function() {        alert('grunt');    }});

This defines a class called Animal, where each animal can have a name and speak. To create a new instance of animal, we just use Ext.create:

上面的代码定义了一个叫Animal的类,每一个animal都有一个名字并可以说话。为了创建一个animal的实例,我们使用Ext.create方法:

var bob = Ext.create('Animal', {    name: 'Bob'});bob.speak(); //alerts 'grunt'

We created an Animal called Bob and commanded it to speak. Now that we have created a class and instantiated it, we can start improving what we have. At the moment we do not know Bob's species, so let us clear that up with a Human subclass:

我们能够创建一个叫Bob的Animal并命令它说话。既然我们已经创建了一个类并实例化了它,我们可以开始改进它了。现在我们不知道Bob的种类,因此我们通过一个Human的子类来让这个清晰化:

Ext.define('Human', {    extend: 'Animal',    speak: function() {        alert(this.getName());    }});

Now we have got a new class that inherits from Animal, therefore gaining all of its functions and configurations. We actually overrode the speak function because most humans are smart enough to say their name instead of grunt. Now, let us make Bob a human:

现在,我们有一个新的类是从Animal继承的,因此,该类继承了Animal的所有配置和方法。我们同样重写了speak方法,因为大部分人类是足以聪明能够说他们的名字而不是呼噜喊叫。现在,让Bob成为一个human:

var bob = Ext.create('Human', {    name: 'Bob'});bob.speak(); //alerts 'Bob'

In the previous example we used a magical function when adding the Human subclass. You will notice that we did not actually define a getName function for our Animal class, so where did it come from? Part of the class system is the ability to give classes configuration options, which each automatically give you the following functionality:

在上面的例子中,当添加Human子类时我们用了一个功能强大的方法。你会发现我们没有为我们的Animal类实际定义一个getName方法,那它来源于哪里呢?类系统关于配置功能的一部分功能如下:

  • a getter function that returns the current value, in this case getName().   返回当前值的getter方法,该例中是getName()
  • a setter function that sets a new value, in this case setName().    设置新值的setter方法,该例中是setName()
  • an applier function called by the setter that lets you run a function when a configuration changes, in this case applyName(). 当一个配置改变后的applier方法,该方法会在setter方法运行后运行,该例中是applyName()
  • an updater function called by the setter than runs when the value for a configuration changes, in this case updateName().     当一个配置改变后的updater方法,该方法会在setter方法调用后调用,该例中是updateName()。

The getter and setter functions are generated for free and represent the recommended way to store data in a class. Every component in Sencha Touch uses the class system and the generated functions always follow the same pattern, so if you know a config, you already know how to get and set its value.

getter、setter方法是自动生成的,并且是类中推荐的储存数据的方法。st中的每一个组件都用到类系统,并且自动生成的方法都遵从相似的形式,因此如果你知道一个配置,你已经知道如何得到和设置它的值了。

This approach also makes your code cleaner. For example, if you wanted to always ask the user if he or she really wants to change Bob's name, you can just define an applyName function that will be called automatically:

这个方法也使得你的代码更加简洁。例如,每当用户改变Bob的名字时,你总想问他是否确定要改变,那么你可以定义一个applyName方法,该方法会被自动调用:

Ext.define('Human', {    extend: 'Animal',    applyName: function(newName, oldName) {        return confirm('Are you sure you want to change name to ' + newName + '?')? newName : oldName;    }});

The previous code sample uses the browser's built in confirm function, which opens a dialog asking the user the question and offering "Yes" and "No" as answers. The applier functions allow you to cancel the name change if the confirm call returned false. As it happens, the confirm function returns either the new or old name, depending on whether the user clicks Yes or No.

上面的代码只是用了浏览器内置的confirm方法,该方法打开一个对话框询问用户并提供yes、no两种答案。当confirm方法返回false时,applier方法允许你取消改变名字的操作。当它发生时,confirm方法返回新值或旧值取决于用户点击的是yes还是no。

If we create a new Bob instance and try to change his name, but then click No when prompted, his name will not change after all:

如果我们创建了一个Bob的实例并试图改变它的名字,当我们点击了弹出窗口中的no时,它的名字不会被改变:

var bob = Ext.create('Human', {    name: 'Bob'});bob.setName('Fred'); //opens a confirm box, but we click Nobob.speak(); //still alerts 'Bob'

The apply function is also a good place where to transform your value. Remember that whatever this returns, this will be the new value for this configuration. A good example of this would be to prepend a title to the name, as shown in the following code sample:

apply方法也是转换值的好地方。记住,无论它返回什么值,这总是该配置的新值。最好的例子是在名字前加一个修饰符,像下面的例子所示:

Ext.define('Human', {    extend: 'Animal',    applyName: function(newName, oldName) {        return 'Mr. ' + newName;    }});

Another config method is update. The update method (updateName() in this case) is only called when the value of the config changes. For example, given the following code, the updateName() will not be called:

另一个配置方法是update。update方法(该例中是updateName())只有在值改变时才调用。例如,下面的代码中,updateName()方法不会被调用:

var bob = Ext.create('Human', {    name: 'Bob'});bob.setName('Bob'); // The name is the same, so update is not called

Consequently, when we use the update method, the config is changing. This function should be the place where you update your component, or do whatever you need to do when the value of your config changes. The following example shows an alert:

因此,当我们使用update方法时,配置就需要改变。该方法是你需要更新组件的地方,或者当配置的值改变时你想做操作的地方。下面的例子展示了一个alert:

Ext.define('Human', {    extend: 'Animal',    updateName: function(newName, oldName) {        alert('Name changed. New name is: ' + newName);    }});

Remember that the update and apply methods get called on component instantiation too, such that in the following example we would get two alerts:

记住,update、applay方法在组件实例化时也会被调用,因此相面的例子中我们会得到两个alerts:

// creating this will cause the name config to update, therefor causing the alertvar bob = Ext.create('Human', {    name: 'Bob'});// changing it will cause the alert to show againbob.setName('Ed');

We have basically already learned the following important behavior of classes:

我们已经学习了类的如下重要行为:

  • All classes are defined using Ext.define, including your own classes   所有的类都是通过Ext.define定义的,包括你自己的类。
  • Most classes extend other classes, using the extend syntax     很多类都是通过extend语法继承自其它类
  • Classes are created using Ext.create, for example Ext.create('SomeClass', {some: 'configuration'})   累世通过Ext.create创建爱你的,例如, Ext.create('SomeClass', {some: 'configuration'})
  • Always use the config syntax to get automatic getters and setters and have a much cleaner codebase   使用config语法得到自动生成的getter、setter方法,并使代码更加简洁。

At this point you can already go about creating classes in your app, but the class system takes care of a few more things that will be helpful to learn.

现在你可以在你的 app中创造类了,但是类系统还有一些值得学习的地方。

Dependencies and Dynamic Loading

依赖和动态加载

Most of the time, classes depend on other classes. The Human class described previously depends on the Animal class because it extends it - we depend on Animal being present to be able to define Human. Sometimes you make use of other classes inside a class, so you need to guarantee that those classes are on the page. This is accomplished using the requires syntax, as shown in the following code sample:

大部分情况下,类依赖于其它类。前面我们所描述的Human雷子依赖于Animal类,因为它继承自Animal类——我们只有在Animal存在的时候才能定义Human类。有时你在一个类中使用其它类,因此你要保证被使用的类存在于页面上。这可以通过使用requires语法来保证,如下所示:

Ext.define('Human', {    extend: 'Animal',    requires: 'Ext.MessageBox',    speak: function() {        Ext.Msg.alert(this.getName(), "Speaks...");    }});

When you create a class this way, Sencha Touch verifies if Ext.MessageBox is already loaded and if not, it immediately loads the required class file with AJAX.

如果你以这种方式创建类,st会验证Ext.MessageBox是否已经加载,如果没有,它会使用AJAX立即加载该类文件。

Ext.MessageBox itself may also have classes it depends on, which are then also loaded automatically in the background. Once all the required classes are loaded, the Human class is defined and you can start using Ext.create to instantiate people. This works well in development mode, as it means you do not have to manage the loading of all your scripts yourself, but not as well in production, because loading files one by one over an internet connection is rather slow.

Ext.MessageBox自己也可能有它所依赖的类,这些类也会在后台被自动加载。一旦所有需要的类都加载完了,Human类就创建完成了,你可以使用Ext.create来实例化人。这在开发模式下很有用,意味着你不需要自己去管理所有脚本的加载,但是在产品中就不好,因为通过网络连接一个个加载文件是相当慢的。

In production, we want to load a single JavaScript file, ideally containing only the classes that our application actually uses. This is done in Sencha Touch using the JSBuilder tool, which analyzes your app and creates a single file build that contains all of your classes and only the framework classes your app actually uses. See the Building guide for details on how to use the JSBuilder.

在产品中,我们需要加载一个单一的js文件,该文件包含我们的应用所用到的所有类。在st中这是通过JSBuilder工具实现的,该工具分析你的app并创建一个包含你的所有类及你的app所用到框架类的单一js文件。查看Building guide查看如何使用JSBuilder。

Each approach has its pros and cons, but can we have the good parts of both without the drawbacks, too? The answer is yes, and we have implemented the solution in Sencha Touch.

每一种方法都有它好的一面和不好的一面,我们能够将每一部分的好的聚合起来而撇开不好的么?答案是yes,并且我们已经在st中有了解决方案。

Naming Conventions

命名规范

Using consistent naming conventions throughout your code base for classes, namespaces, and filenames helps keep your code organized, structured, and readable.

在类中使用统一的命名规范:命名空间.文件名能够保证你代码的整洁性和可读性。

1) Classes

Class names may only contain alphanumeric characters. Numbers are permitted but are discouraged in most cases, unless they belong to a technical term. Do not use underscores, hyphens, or any other non-alphanumeric character. For example:

类名字只能包含字母和数字。数字是允许的但是不建议使用数字,除非这些类属于工具类。不要使用下划线,连字符或其它非字母数字的字符,例如:

  • MyCompany.useful_util.Debug_Toolbar is discouraged                       MyCompany.useful_util.Debug_Toolbar是不被鼓励的
  • MyCompany.util.Base64 is acceptable                     MyCompany.util.Base64是可接受的

Where appropriate, class names should be grouped into packages and should be properly namespaced using the object property dot notation ( . ). At the minimum, there should be one unique top level namespace followed by the class name. For example:

在适当的地方,类名字应该以包名字分组,并且添加正确的命名空间。在最极端情况下,类名字应该有一个顶层的唯一命名空间。例如:

MyCompany.data.CoolProxyMyCompany.Application

The top level namespaces and the actual class names should be in CamelCase, everything else should be all lower-cased. For example:

顶层的命名空间及实际的类名应该是驼峰的形式,其它的都应该是小写的,例如:

MyCompany.form.action.AutoLoad

Classes that are not distributed by Sencha should never use Ext as the top-level namespace.

非st提供的类顶层命名空间不应该使用Ext。

Acronyms should also follow the CamelCase convention, as illustrated by the following naming examples:

首字母缩略词也应该遵从驼峰式的命名规范,如下面的命名例子所示:

  • Ext.data.JsonProxy instead of Ext.data.JSONProxy             Ext.data.JsonProxy 而不是 Ext.data.JSONProxy
  • MyCompany.util.HtmlParser instead of MyCompary.parser.HTMLParser     MyCompany.util.HtmlParser 而不是MyCompary.parser.HTMLParser
  • MyCompany.server.Http instead of MyCompany.server.HTTP      MyCompany.server.Http 而不是 MyCompany.server.HTTP

2) Source Files

源文件

The names of the classes map directly to the file paths in which they are stored. As a result, there must only be one class per file. For example:

类名字与存储他们的文件路劲是相对应的。因此,每一个文件只有一个类定义。例如:

  • Ext.mixin.Observable is stored in path/to/src/Ext/mixin/Observable.js
  • Ext.form.action.Submit is stored in path/to/src/Ext/form/action/Submit.js
  • MyCompany.chart.axis.Numeric is stored in path/to/src/MyCompany/chart/axis/Numeric.js

where path/to/src is the directory of your application's classes. All classes should stay under this common root and should be properly namespaced for the best development, maintenance, and deployment experience.

path/to/src是你的应用的类目录。所有的类都应该位于该跟路劲下,为了获得最好的开发、维护、发布体验,都应该给与正确的命名空间。

3) Methods and Variables     方法和变量

Similarly to class names, method and variable names may only contain alphanumeric characters. Numbers are permitted but are discouraged in most cases, unless they belong to a technical term. Do not use underscores, hyphens, or any other nonalphanumeric character.

与类名类似,方法、变量名也只能包含数字和字母。数字是允许的但是在大部分情况下不建议使用,除非该方法属于功能性的方法。不要使用下划线、连字符及其他非字母数字的字符。

Method and variable names should always use CamelCase. This also applies to acronyms.

方法和变量名应该使用驼峰式命名。这也同样适用于首字母简写。

Here are a few examples:

这里有一些例子:

  • Acceptable method names: 可接受的方法名:

    encodeUsingMd5()getHtml() instead of getHTML()getJsonResponse() instead of getJSONResponse()parseXmlContent() instead of parseXMLContent()
  • Acceptable variable names:   可接受的变量名:

    var isGoodNamevar base64Encodervar xmlReadervar httpServer

4) Properties  属性

Class property names follow the same convention as method and variable names, except the case when they are static constants. Static class properties that are constants should be all upper-cased, for example:

类的属性名与方法和变量遵从同样的命名规范,除非他们是静态常量。类的静态常量应该全部是大写的,例如:

  • Ext.MessageBox.YES = "Yes"
  • Ext.MessageBox.NO = "No"
  • MyCompany.alien.Math.PI = "4.13"

Working with Classes in Sencha Touch 2

使用st中类

1. Declaration   声明

1.1. The Old Way  原始方法

If you have developed apps with Sencha Touch 1.x, you are certainly familiar with the Ext.extend function that creates a class:

如果你使用st 1.x开发,你对Ext.define方法创建类应该很熟悉:

var MyPanel = Ext.extend(Object, {    // ...});

This approach is easy to follow when creating a new class that inherits from another. Other than direct inheritance, however, there was not a fluent API for other aspects of class creation, such as configuration, statics, and mixins. We will be reviewing these items in detail shortly.

该方法在创建一个继承自其它类的类时很容易。不同于直接继承,针对类创建的其它方面,如配置、静态成员、混合模式,没有一个方便的api类来进行配置管理。我们简要的来看下这些子项的细节。

Let us take a look at the following example:

看如下的例子:

My.cool.Panel = Ext.extend(Ext.Panel, {    // ...});

In this example we want to namespace our new class and make it extend from Ext.Panel. There are two concerns we need to address:

在这个例子中,我们想给新类一个命名空间,并让它继承自Ext.Panel。我们需要关注两方面:

  1. My.cool needs to be an existing object before we can assign Panel as its property.         在我们将Panel作为My.cool的属性时,My.cool应该先存在
  2. Ext.Panel needs to exist or to be loaded on the page before it can be referenced.         Ext.Panel在引用前应该已经存在或者可加载至页面上

The first concern is usually solved by using Ext.namespace (aliased by Ext.ns). This method recursively traverses the object/property tree and creates them if they do not exist yet. The drawback is that you need to remember adding this function call above Ext.extend all the time, as shown in the following example:

第一个关注点通常通过使用Ext.namespace(别名是Ext.ns).该方法低对象/属性树递归的调用,如果它们不存在则创建它们。缺点是你需要在Ext.extend方法之前调用下该方法,如下所示:

Ext.ns('My.cool');My.cool.Panel = Ext.extend(Ext.Panel, {    // ...});

The second issue, however, is not easy to address because Ext.Panel might depend on many other classes that it directly or indirectly inherits from, and in turn, these dependencies might depend on other classes to exist. For that reason, applications written before Sencha Touch 2 usually include the whole library in the form of ext-all.js, even though they might only need a small portion of the framework.

第二点,却不大好保证,因为Ext.panel可能依赖于很多直接或间接的从其它类继承的类,同样的,这些类也可能依赖其它类。一次,st2之前的应用通常以ext-all.js的形式包含所有的库,经管他们可能只需要框架的很小一部分。

1.2. The New Way   新的方式

Sencha Touch 2 eliminates all these drawbacks with a single method you need to remember for class creation: Ext.define. Its basic syntax is as follows:

st2通过一个方法解决了上面所有的缺点,那就是通过Ext.define定义类。它的基本语法如下:

Ext.define(className, members, onClassCreated);

Let us look at each part of method call:

让我们看看该方法的每一部分:

  • className is the class name    类名
  • members is an object that represents a collection of class members in key-value pairs  代表类成员的集合,键值对的形式
  • onClassCreated is an optional function callback to be invoked when all dependencies of this class are ready, and when the class itself is fully created. Due to the new asynchronous nature of class creation, this callback can be useful in many situations. These will be discussed further in Section IV.     onClassCreated是一个可选的回调函数,该函数在所有的依赖被加载后、类被创建时调用。由于类的创建是yibu的,该回调函数在很多情况下是有用的。这会在第四部分讲解。

Example   例子

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 aaron = Ext.create('My.sample.Person', 'Aaron');aaron.eat("Salad"); // alert("Aaron is eating: Salad");

Note that we created a new instance of My.sample.Person using the Ext.create() method. We could have used the new keyword (new My.sample.Person()). However it is recommended that you always use Ext.create, since it allows you to take advantage of dynamic loading. For more info on dynamic loading see the Getting Started guide.

注意,我们通过Ext.create()创建了My.sample.Person的实例。我们可能会使用new关键词(new  My.sample.Person())。然后,推荐你使用Ext.create,因为它可以从分利用动态加载的优点。关于动态加载的更多信息,查看Getting Started guide。

2. Configuration   配置

In Sencha Touch 2, we introduce a dedicated config property that is processed by the powerful Ext.Class preprocessors before the class is created. The config features include the following:

在st2中,我们介绍了一个很强大的config配置属性,该属性在类创建前,有强大的Ext.Class预处理程序进行处理。

  • Configurations are completely encapsulated from other class members.       配置是被其他类成员完全包裹的
  • Getter and setter functions, methods for every config property are automatically generated into the class prototype during class creation, if the class does not have these methods already defined.        每一个配置的getter、setter方法在类创建的过程中是自动创建到类邹型中的,如果该类没有定义这些方法。
  • An apply method is also generated for every config property. The auto-generated setter method calls the apply method internally before setting the value.     Override the apply method for a config property if you need to run custom logic before setting the value. If apply method does not return a value, then the setter will not set the value. For an example see the following applyTitle code sample:                   

     每一个config的属性也会自动生成apply方法。自动生成的setter方法在设置值前会调用apply方法。在设置值前如果你想运行自定义逻辑,那么就重写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); }, applyTitle: function(title) {     if (!Ext.isString(title) || title.length === 0) {         console.log('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);         }     } }

    });

The following code illustrates the use of the previously defined My.own.Window class:

下面的代码说明了如何使用上面定义的My.own.Window类:

var myWindow = Ext.create('My.own.Window', {    title: 'Hello World',    bottomBar: {        height: 60    }});console.log(myWindow.getTitle()); // logs "Hello World"myWindow.setTitle('Something New');console.log(myWindow.getTitle()); // logs "Something New"myWindow.setTitle(null); // logs "Error: Title must be a valid non-empty string"myWindow.setBottomBar({ height: 100 }); // Bottom bar's height is changed to 100

3. Statics   静态成员

Static members can be defined using the statics config, as shown in the followind example:

静态成员可以通过使用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    },    constructor: function(config) {        this.initConfig(config);        // the 'self' property of an instance refers to its class        this.self.instanceCount ++;    }});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"

Error Handling and Debugging

错误处理与调试

Sencha Touch 2 includes some useful features that help you with debugging and error handling.

st2包含了一些帮助你调试及处理错误的特性。

You can use the Ext.getDisplayName() call to get the display name of any method. This is especially useful for throwing errors that have the class name and the method name in their description, as shown in the following code sample:

你可以使用Ext.getDisplayName()方法获得任何方法的显示名称。这通常在那些有类名字及方法名抛出错误的地方非常有用,像如下代码所示:

throw new Error('['+ Ext.getDisplayName(arguments.callee) +'] Some message here');

When an error is thrown in any method of any class defined using Ext.define(), you should see the method and class names in the call stack if you are using a WebKit based browser (Chrome or Safari). For example, here is what it would look like in Chrome:

当通过Ext.define()定义的任何类的任何方法抛出一个错误是,如果你用的是webkit内核的浏览器(chrome 或者safari),你应该在调用栈里看方法名和类名。例如,下面是chrome浏览器中的示例:

Further Reading

延伸阅读

Classes are only a part of the Sencha Touch 2 ecosystem. To understand more about the framework and how it works, we recommend reading the following guides:

  • Components and Containers
  • The Data Package
  • The Layout System
  • Getting Started
这几次的文章每次都是第二天来补的,哎,深感时间不够用,每天都感觉很累的样子,还是继续加油吧,要学习提高的还有很多很多!fighting,fighting!!!!!!!!!!!
原创粉丝点击