13.typescript-类
晚上睡不着 早上睡不着 中午睡不着 想睡觉
<----------------------------------------☺mizhiweixiao☺---------------------------------------->
1.类的相关概念
(1)类(Class):定义了一件事物的抽象特点,包含它的属性和方法。
(2)对象(Object):类的实例,通过new生成。
(3)面向对象(OOP)的三大特性:封装,继承,多态。
(4)封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据。
(5)继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
(6)多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。
比如:Cat和Dog都继承自Animal,但是分别实现了自己的eat方法。此时针对某一个实例,我们无需了解它是Cat还是Dog,就可以直接调用eat方法,程序会自动判断出来应该如何执行eat。
(7)存取器(getter&setter):用以改变属性的读取和赋值行为。
(8)修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。
比如public表示公有属性或方法。
(9)抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中实现。
(10)接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类智能继承自另一个类,但是可以实现多个接口。
2.类
举个栗子
1 // 声明一个Students类(首字母大写):这个类里面有三个成员 2 class Students{ 3 // 一个name属性 4 name : string; 5 // 一个构造函数 6 constructor(message:string){ 7 this.name = message; 8 }; 9 // 一个sayHi方法发 10 sayHi(){ 11 return "Hi "+this.name; 12 }; 13 14 } 15 16 // 构造了Students类的一个实例 17 var getStu = new Students("weizeyang");
下面我们来解剖下上面的代码
(1)使用class关键字声明类,且类的首字母大写
(2)使用constructor声明构造函数
构造函数这个家伙要讲一讲,之前学java的时候是这样的:
a.构造函数要和类同名, -> constructor已经足够
b.构造函数个数不限定, -> 我试了下,说是为了准确不允许多个
c.构造函数中的参数不限定,
d.当类被实例化时,先执行构造函数,
e.类内没有自定义构造函数的,系统会给一个默认的空的构造函数,
f.构造函数没有返回值,
综上,我试了下,除上面的a、b不支持外,其余应该都是支持的
(3)用this引用类成员
(4)用new构造类的实例(也可以叫对象)
(5)上面提到过new生成实例时会自动调用构造函数,那么传的参数自然是给构造函数的
1 class Students{ 2 3 name : string; 4 // 在构造函数里面添加一个参数 5 constructor(message:string,age:number){ 6 this.name = message; 7 }; 8 9 sayHi(){ 10 return "Hi "+this.name; 11 }; 12 13 } 14 15 var getStu = new Students("weizeyang"); 16 // error :supplied parameters do not match any signature of call target
这个时候会报错,告诉我们说参数匹配不上
3.继承
举个栗子
1 // 父类 2 class College{ 3 department : string; 4 constructor(name:string){ 5 this.department = name; 6 }; 7 ceremony(num:number = 1){ 8 console.log(`本次典礼${this.department}共有${num}人参加`); 9 } 10 } 11 12 // 继承1 13 class Mathematical extends College{ 14 constructor(name:string,num:number){ 15 super(name); 16 this.ceremony(num); 17 }; 18 own(owntype:string,ownnum:number){ 19 console.log(`${this.department}自己举办了一场${ownnum}人的典礼`); 20 } 21 } 22 23 // 继承2 24 class Exploration extends College{ 25 constructor(name:string){ 26 super(name); 27 }; 28 // 重写了父类的方法 29 ceremony(num = 2){ 30 console.log("<--------不要脸的分割线------>"); 31 super.ceremony(num); 32 } 33 } 34 35 let college1 = new Mathematical("数理学院",3); 36 // 本次典礼数理学院共有3人参加 37 college1.own("数理",5); 38 // 数理学院自己举办了一场5人的典礼 39 40 let college2 = new Exploration("勘查技术学院"); 41 college2.ceremony(4); 42 // <--------不要脸的分割线------> 43 // 本次典礼勘查技术学院共有4人参加 44 45 let college3 = new Exploration("勘查技术学院"); 46 college2.ceremony(); 47 // <--------不要脸的分割线------> 48 // 本次典礼勘查技术学院共有2人参加
这段代码有点长啊!但是,慢慢看来其实也并不长~
(1)使用关键字extends继承父类。
(2)子类可以访问父类的属性或方法。
(3)包含constructor函数的派生类必须调用super(),它会执行父类的构造函数。
(4)继承2中创建了ceremony方法,重写(覆盖)了从College继承来的ceremony方法,使得ceremony方法根据不同的类而具有不同的功能。
(5)重写的概念:也叫覆盖,指在子类中定义一个和父类方法返回值、方法名、参数列表完全一致的方法。
(6)super应用的两种语法:
1)constructor内的super():执行父类的构造函数。必须至少执行一次。
2)一般方法内的super.method():执行父类的(未必同名的)方法。不是必须的。
(7)继承1中创建了除继承外的自己的own方法,并在里面使用了从父类继承了的department属性。
4.常用的修饰符
(1)public
* 公有,public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
* 在typescript中,每个成员默认为public的,即可以自由的访问程序里面定义的成员。
1 class College{ 2 public department : string; 3 public constructor(name:string){ 4 this.department = name; 5 }; 6 public ceremony(num:number = 1){ 7 console.log(`本次典礼${this.department}共有${num}人参加`); 8 } 9 }
是和上面所举的例子是一样的
(2)private(私有)
* 私有,就是除了类自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
* 当成员被标记成private时,它就不能在声明它的类的外部访问(包括继承它的子类中)。
1 class College{ 2 private department : string; 3 constructor(name:string){ 4 this.department = name; 5 }; 6 ceremony(num:number = 1){ 7 console.log(`本次典礼${this.department}共有${num}人参加`); 8 } 9 } 10 11 class Mathematical extends College{ 12 constructor(name:string,num:number){ 13 super(name); 14 this.ceremony(num); 15 }; 16 own(owntype:string,ownnum:number){ 17 console.log(`${this.department}自己举办了一场${ownnum}人的典礼`); // error 18 } 19 } 20 21 new College("数理").department; // error
上面的栗子17和21行报错 property 'departement' is private and only accessible within class 'College' ,意思是私有的departement这个变量只能在College这个类里面使用。
* 共享了私有变量的派生类实例和该类的实例可以进行相互赋值,但是同为含有私有变量的类的实例不可以与该类进行赋值。
1 class College{ 2 private department : string; 3 constructor(name:string){ 4 this.department = name; 5 }; 6 } 7 8 class Mathematical extends College{ 9 constructor(name:string){ 10 super(name); 11 }; 12 } 13 14 class Exploration{ 15 private department : string; 16 constructor(name:string){ 17 this.department = name; 18 }; 19 } 20 21 var college1 = new College("1"); 22 var college2 = new Mathematical("1"); 23 var college3 = new Exploration("1"); 24 25 college1 = college2; 26 college1 = college3; // error 27 college2 = college3; // error
这是26行报的错,27行同
type 'Ecploration' is not assignable to type 'Mathematical' .
types have separate declarations of a private property 'departemt'.
意思是Ecploration不能给Mathematical赋值,因为departemt是私有的
(3)protected
* 受保护的,protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
* protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。
1 class College{ 2 protected department : string; 3 constructor(name:string){ 4 this.department = name; 5 }; 6 ceremony(num:number = 1){ 7 console.log(`本次典礼${this.department}共有${num}人参加`); 8 } 9 } 10 11 class Mathematical extends College{ 12 constructor(name:string,num:number){ 13 super(name); 14 this.ceremony(num); 15 }; 16 own(owntype:string,ownnum:number){ 17 console.log(`${this.department}自己举办了一场${ownnum}人的典礼`); 18 } 19 } 20 21 new College("数理").department; // error
参照上面(2)私有的第一个栗子。
(4)参数属性
依旧拿上面的例子搞事情
1 class College{ 2 constructor(private name:string){}; 3 ceremony(num:number = 1){ 4 console.log(`本次典礼${this.name}共有${num}人参加`); 5 } 6 } 7 8 let college = new College("数理"); 9 college.ceremony(); 10 11 // 本次典礼数理共有1人参加
这样就减少了类中属性department的定义,在构造函数的使用了private name:string参数来创建和初始化name成员。我们把声明和赋值放在了一起。
参数属性通过给构造函数参数添加一个访问限定符来声明。使用private限定一个参数属性会声明并初始化一个私有成员。
对于public和protected也是一样的,但是要注意的是上面讲过成员默认的就是public的,但是在这里要显示的出现public才有效。
5.存取器
* 使用getter和setter可以改变属性的赋值和读取行为。
下面的这个例子是从http://www.cnblogs.com/tansm/p/TypeScript_Handbook_Classes.html这里copy过来的....
下面这个例子里,我们先检查用户密码是否正确,然后再允许其修改employee。 我们把对fullName的直接访问改成了可以检查密码的set
方法。 我们也加了一个get
方法,让上面的例子仍然可以工作。
1 var passcode = "secret passcode"; 2 3 class Employee { 4 private _fullName: string; 5 6 get fullName(): string { 7 return this._fullName; 8 } 9 10 set fullName(newName: string) { 11 if (passcode && passcode == "secret passcode") { 12 this._fullName = newName; 13 } 14 else { 15 console.log("Error: Unauthorized update of employee!"); 16 } 17 } 18 } 19 20 var employee = new Employee(); 21 employee.fullName = "Bob Smith"; 22 if (employee.fullName) { 23 alert(employee.fullName); 24 }
我们可以修改一下密码,来验证一下存取器是否是工作的。当密码不对时,会提示我们没有权限去修改employee。
注意:若要使用存取器,要求设置编译器输出目标为ECMAScript 5或更高。
对,我的就是因为这个报错了.... error : Accessors are only available when targeting ECMAScript 5 and higher.
6.静态属性
下面换个口味,用一个求环面柱体的体积的例子了解下静态属性和静态方法的使用
1 class Volume{ 2 // 定义了一个静态属性 3 static heigth = 10; 4 5 // 声明并定义了一个静态函数 6 static calculateVolume(secondRadius:number) { 7 8 // 使用类的实例化成员smallRadius 9 let smallRadius = new Volume(8).smallRadius; 10 let area; 11 if(secondRadius> smallRadius){ 12 area = Math.PI*(secondRadius*secondRadius - smallRadius*smallRadius); 13 } 14 15 // 使用类的静态属性 16 return area*Volume.heigth; 17 } 18 19 constructor (private smallRadius: number) { 20 if(smallRadius>5){ 21 smallRadius = 5; 22 } 23 } 24 } 25 26 // 调用了类的静态方法 27 console.log(Volume.calculateVolume(9));
(1) 用static修饰符修饰的方法或属性称为静态方法或静态属性
(2)静态方法或属性不需要实例化,而是用 类.静态属性 的方式调用
(3)在静态方法里面访问类的实例成员->9行
再贴一个在网上看见的例子
1 class Person{ 2 private static _instance:Person; 3 4 static getInstance():Person{ 5 if(this._instance == null){ 6 this._instance = new Person(); 7 } 8 return this._instance; 9 } 10 11 talk(){ 12 alert("Hi, my name is wzy!"); 13 } 14 } 15 16 function run(){ 17 Person.getInstance().talk(); 18 } 19 20 run(); // 这里执行了12行的alert
7.抽象类
我照着上面在网上贴的例子改了下
其中发现了一些问题
1 abstract class Person{ 2 _instance:Person; 3 constructor(public name:string){} 4 abstract public getInstance():string; 5 6 private talk(){ 7 return `my name is ${this.name}`; 8 } 9 } 10 11 class Student extends Person{ 12 constructor(name:string){ 13 super(name); 14 } 15 getInstance(){ 16 this._instance = new Person("wzy"); // error : Person是抽象类,不能被实例化 17 this._instance.talk(); // error : 这里有三个错误 18 } // (1)Person不能被实例化,所以不能调用它里面的方法 19 } // (2)即使被实例化了也不能调用这个方法,因为talk这个方法是私有的,不能在Person类外面使用 20 // (3)getInstance这个方法实现的有错误,没有string返回值
21 var stu = new Student("weizeyang");
22 console.log(stu.getInstance());
改正了一下,有了下面的这个例子
1 class Good{ 2 constructor(public name:string){} 3 talk(){ 4 return `${this.name} is good student`; 5 } 6 } 7 8 // 抽象类 9 abstract class Person{ 10 // Good类型的属性 11 _instance:Good; 12 // 抽象类中的抽象方法 13 abstract getInstance():string; 14 } 15 16 // 继承了抽象类 17 class Student extends Person{ 18 // 实现了基类中的抽象方法 19 getInstance(){ 20 this._instance = new Good("wzy"); 21 return this._instance.talk(); 22 } 23 } 24 25 var stu = new Student(); 26 console.log(stu.getInstance());
(1)使用abstract定义抽象类和其中的抽象方法
(2)抽象类不允许被实例化
(3)抽象类中的抽象方法必须被子类实现
(4)抽象方法的语法和接口方法相似,两者都是只声明不定义,即不包含方法体
8.类的类型
1 class Students{ 2 name : string; 3 constructor(message:string){ 4 this.name = message; 5 }; 6 sayHi(){ 7 return "Hi "+this.name; 8 }; 9 } 10 11 // 给了getStu一个Students这个类的类型 12 var getStu:Students; 13 getStu = new Students("weizeyang");
类的这一篇终于要结束了,为了首尾呼应,最后用了本篇最开始的栗子
<----------------------------------------☺mizhiweixiao☺---------------------------------------->
类的这一篇写的,真是把我的老血都熬出来了