Sencha touch 开发系列:类系统

类是啥玩意呢?我想玩过面象对象语言的人,对它一点也不会陌生了。

我还是重申下这玩意吧

类是对象概念在面向对象编程语言中的反映,是相同对象的集合,类描述了一系列在概念上有相同的含义的对象,并为这些对象同一定义了编程语言上的属性和方法。例如:动物是一个类,狗、鸡、猪也是类。但是他们的具体的属性和方法可能不完全相同。

ST中有强大的类系统定义模型,可以很轻松的创建javascript类。

ST中大家用到的东西都已封装成类,如:组件/容器,事件模型,数据模型,数据存储,控制器......等等!

下面我们就来讲讲,如何定义类?

其实,你在学习组件,的时候已经用到了!

用Ext.define来声明一个类

//声明一个动物类
Ext.define('Animal', {  
    //字段
    name: null,
    age:null,
    constructor: function(name,age) {
        //构造中给字段复制
        this.name=name;
        this.age=age;
    },
    //说hello方法
    sayName: function() {
        alert("hello 我的名字叫:"+this.name+",我今年:"+this.age+"岁!");
    }
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal("小黑",5);
//动物介绍自己
animal.sayName();

动运行,会打印出 hello 我的名字叫小黑,我今年:5岁!

很简单吧,和C#,java这类语言类似了是不?

当我们要访问类字段的时候,也很简单如:

animal.name

你也可以给字段复值

animal.name="王八蛋"

有同学会问了,有没有像我们C#,java中属性的概念呢?

就是通过get,set访问和设置器进行操作呢

那在ST里面如何做到呢?ST也确实为我们提供了一个这样的机制。

看代码:

//声明一个动物类
Ext.define('Animal', {  
    //字段    
    age:null,
    //属性
    config:
    {
       name: null,
    },
    //构造器
    constructor: function(age,config) {
        //构造中给字段复制       
        this.age=age;
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("hello 我的名字叫:"+this.getName()+" 我今年:"+this.age+"岁!");
    }
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal(5,{name:'小黑'});
//动物介绍自己
animal.sayName();

输出和上面的一样的了,只是我在这里做了下改动,把age定义为字段,把name定义为属性

注意看属性的定义及访问方式

属性必须放在config下面在构告函数中可调用initConfig进行统一属性配置。

也可以在外部通过调用get+"属性名[首字母大定]"或set+"属性名[首字母大定]"进行访问,配置。

下在我将上面例子改下。

//声明一个动物类
Ext.define('Animal', {  
    //字段    
    age:null,
    //属性
    config:
    {
       name: null,
    },
    //构造器(空造函数)
    constructor: function() {       
    },
    //说hello方法
    sayName: function() {
        alert("hello 我的名字叫:"+this.getName()+" 我今年:"+this.age+"岁!");
    }
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal();
//访问字段进行年年龄设置
animal.age=5;
//访问属性进行名字设置
animal.setName("小黑");
//动物介绍自己
animal.sayName();

                             

看,输出还是一样。

再看一段:

//声明一个动物类
Ext.define('Animal', {  
    //字段    
    age:null,
    //属性
    config:
    {
       name: null,
    },
    //构造器(空造函数)
    constructor: function(age,name) { 
        //字段进行初始化
        this.age=age;
        //属性进行初始化
        this.setName(name);
    },
    //说hello方法
    sayName: function() {
        alert("hello 我的名字叫:"+this.getName()+" 我今年:"+this.age+"岁!");
    }
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal(5,"小黑");
//动物介绍自己
animal.sayName();

                             

输出仍然是一样的!

你看到没有,我们可以很方便,很灵活的创建使用类。

但你会发现,太灵活也悲催了,使用者需要去确认哪些是类字段,哪些是属性。

如果你定义了一个类,使用者都需要看你的类描述才能进行构造它,创建它,怕构造的时候传错。

ST提倡一种标准的声明法使用属性,使用者只需要在创建的时候传或不传config访问操作的时候一律使用get,set 访问器。

如;

//声明一个动物类
Ext.define('Animal', {      
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("hello 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    }
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal({name:"小黑",age:5});
//动物介绍自己
animal.sayName();

输出还是一样,但规范了很多,至少创建者只需要关心config有哪些方法有哪些就好!

这时,那个叫王八蛋的动物发彪了,它抗议不通随便给叫这样的名字,必须禁止这样的情况出现?

确实,在现实开发中,一些属性字段,可能会有一些业务或者边界上的检查,比如年龄这样的属性,如果这个动物是狗你给来个1000岁,那都成精了是不?

所在以赋值的时候就必须进行检查。

看看代码我们是如何做到属性控制的呢?

//声明一个动物类
Ext.define('Animal', {      
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        console.log("hello 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
    //在赋值名字前
    applyName:function(name)
    {
        //在此如果我们没有任何检查可以直接返回name,但我们需要做名字的检查,如果名字是王八蛋
        //那么就必须反抗,否则才接受
        if(name=="王八蛋")
        {
            console.log('你太不厚道了,不能给我取这样的名字');
        }
        else
        {
            return name;
        }
    }
    
});
//创建一个动物类的实例(产生一个动物)
var animal=new Animal({name:"小黑",age:5});
//动物介绍自己
animal.sayName();
//给它改名叫王八蛋
animal.setName("王八蛋");
//再看的新名字
console.log(animal.getName());
//给个非王八蛋的名字
animal.setName("大黑");
//再看的新名字
console.log(animal.getName());


                             

我们F12看看控制台输出的结果:

hello 我的名字叫:小黑 我今年:5岁!
你太不厚道了,不能给我取这样的名字
小黑
大黑
 
可以看出,当我们给它改名为王八蛋的时候,它反抗了,名字没有改成功。后面我们让它叫大黑,它就接受了!
呵呵,学会了点么?多去练习下撒
 
 

下面继续玩玩ST类的特性:继承

ST里面继承可通过extend配置

如:

//声明一只狗
Ext.define('Dog', {
  //狗中从动物继承    
    extend:'Animal',  
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("超级狗 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
   //狗叫
    bark:function()
    {
        alert("狗叫了");
     }

});

看代码备注,这个extend是不是很熟悉,我们写组件,view的时候都有用到!这里通过extend关键字,让动物类派生了一个具特的动物狗类(狗有很多品种,每个品种都有些不一样的地方,你可以让狗再派生出哈巴狗或啥的)

上述代码,添加了一个bark方法和复写了sayName方法

JS是一种弱类型语言,所以没有java,c#在类应用上这么直观。你只要慢慢习惯就好了.....

类的别名与类的名称空间及类的创建

 

//声明一个动物类【名称空间为Deom】
Ext.define('Demo.Animal', {  
     //创建动物类的别名
    alias: 'widget.animal',
   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        console.log("hello 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
    //在赋值名字前
    applyName:function(name)
    {
        //在此如果我们没有任何检查可以直接返回name,但我们需要做名字的检查,如果名字是王八蛋
        //那么就必须反抗,否则才接受
        if(name=="王八蛋")
        {
            console.log('你太不厚道了,不能给我取这样的名字');
        }
        else
        {
            return name;
        }
    }
    
});
//声明一只狗【名称空间为Demo】
Ext.define('Demo.Dog', {
  //狗中从动物继承    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.dog',
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("超级狗 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
   //狗叫
    bark:function()
    {
        alert("狗叫了");
     }

});
//我们通过类全名(带上名称空间)创建类
var dog=new Demo.Dog();
dog.bark();
//我们通过别名创建类
var dog1=Ext.widget('dog');
dog1.bark();
//我们通过ST的通用静态方法创建类[创是需要类的全路径]
var dog2=Ext.create("Demo.Dog");
dog2.bark();    

还是注意看代码备注了,类的名称空间:是为了解决类名冲突

类的别名:为了类使用更简单更好记

 

下面我们再讲下在类里面如何添加事件

ST为封装了很好的事件模型接口mixins: ['Ext.mixin.Observable']

我们能过扩展事件接口,就可以去点火事件了。

比下我们下面来为狗添加一个dogbark事件,当你调用bark方法的时候,会去执行dogbark事件

看代码:

//声明一个动物类【名称空间为Deom】
Ext.define('Demo.Animal', {  
     //创建动物类的别名
    alias: 'widget.animal',
   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        console.log("hello 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
    //在赋值名字前
    applyName:function(name)
    {
        //在此如果我们没有任何检查可以直接返回name,但我们需要做名字的检查,如果名字是王八蛋
        //那么就必须反抗,否则才接受
        if(name=="王八蛋")
        {
            console.log('你太不厚道了,不能给我取这样的名字');
        }
        else
        {
            return name;
        }
    }
    
});
//声明一只狗【名称空间为Demo】
Ext.define('Demo.Dog', {
  //狗中从动物继承    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.dog',
    //内联继承事件接口
    mixins: ['Ext.mixin.Observable'],
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("超级狗 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
   //狗叫
    bark:function()
    {
        console.log("狗叫了");
        //触发狗叫事件
        this.fireEvent("dogbark",this);
     }

});
//我们通过ST的通用静态方法创建类[创是需要类的全路径]
var dog=Ext.create("Demo.Dog");
//添加狗叫监听
dog.on("dogbark",function(e){
	alert("狗真的叫了!");
});
//调用狗叫方法
dog.bark();

上面代码是alert出:狗真的叫了! 你可以试试!

然后你再看看代码,我们是如何添加事件的?

下面我们来一个综合点的例子:利用事件完成一个经典的设计模式:观察者模式

狗叫了:猫叫了,老鼠跑了,主人醒了!实现这个连动效果场景。

还是例用上面的代码!

//声明一个动物类【名称空间为Deom】
Ext.define('Demo.Animal', {  
     //创建动物类的别名
    alias: 'widget.animal',
   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        console.log("hello 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
    //在赋值名字前
    applyName:function(name)
    {
        //在此如果我们没有任何检查可以直接返回name,但我们需要做名字的检查,如果名字是王八蛋
        //那么就必须反抗,否则才接受
        if(name=="王八蛋")
        {
            console.log('你太不厚道了,不能给我取这样的名字');
        }
        else
        {
            return name;
        }
    }
    
});
//声明一只狗【名称空间为Demo】
Ext.define('Demo.Dog', {
  //狗从动物继承    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.dog',
    //内联继承事件接口
    mixins: ['Ext.mixin.Observable'],
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },
    //说hello方法
    sayName: function() {
        alert("超级狗 我的名字叫:"+this.getName()+" 我今年:"+this.getAge()+"岁!");
    },
   //狗叫
    bark:function()
    {
        console.log("狗叫了!");
        //触发狗叫事件
        this.fireEvent("dogbark",this);
     }

});


//声明一只猫【名称空间为Demo】
Ext.define('Demo.Cat', {
  //猫也从动物继承    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.cat',   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },   
   //猫叫了
    bark:function()
    {
        console.log("猫叫了!");
     }

});

//声明一只老鼠【名称空间为Demo】
Ext.define('Demo.Mice', {
  //老鼠也从动物继承    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.mice',   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },   
   //老鼠跑路
    run:function()
    {
        console.log("老鼠跑路了!");
     }

});

//声明一只主人【名称空间为Demo】
Ext.define('Demo.Human', {
  //人也从动物继承【传说人类也是动物】    
    extend:'Demo.Animal',
    //创建狗类的别名
    alias: 'widget.human',   
    //属性
    config:
    {
       name: null,
       age:null,
    },
    //构造器
    constructor: function(config) { 
        //初始化属性
        this.initConfig(config);
    },   
   //主人醒了
    wake:function()
    {
        console.log("主人醒了!");
     }

});
//这里动作的触发者是狗
var dog=Ext.widget("dog");

//猫听到狗叫了,也叫了
var cat=Ext.widget("cat");
//监听到狗叫
dog.on("dogbark",cat.bark);


//老鼠听到狗叫了,吓跑了
var mice=Ext.widget("mice");
//监听到狗叫
dog.on("dogbark",mice.run);

//主人听到狗叫,吵醒了
var human=Ext.widget("human");
//监听到狗叫
dog.on("dogbark",human.wake);

//狗开始叫了
dog.bark();

输出的结果是:

是不是一连串效应产生了,这就是事件的魅力。

好了,这节就搞这么多了吧,下一篇我们将继续讲解事件,及他的用法!

有问题的同学可以加入我们的社区或群:13453484在线提问,我尽速解答。

作者:Louja
出处:http://html5mob.cnblogs.com 同步在:http://html5mob.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此声明,且在文章页面给出原文连接,否则保留追究法律责任的权利。 

posted @ 2012-07-14 14:44  HTML5MOB  阅读(2379)  评论(0编辑  收藏  举报