TypeScript面向对象超详细分析
作者:橘猫吃不胖~
1 面向对象原则
- 单一原则:一个类只负责一个职责。
- 里氏替换原则:子类可以在任何地方替换它的父类。
- 依赖倒置原则:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。
- 接口隔离原则:提供尽可能小的单独接口,而不要提供大的总接口。暴露行为让后面的实现类知道的越少越好。
- 迪米特法则:尽量降低类与类之间的耦合。
- 开闭原则:面向扩展开放,面向修改关闭。
- 组合/聚合复用原则:尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
2 TypeScript类
TypeScript 是面向对象的 JavaScript。类描述了所创建的对象共同的属性和方法。TypeScript 支持面向对象的所有特性。
2.1 类的定义
TypeScript使用class
关键字定义类。
class 类名 {
// 具体代码
}
类可以包含以下几个模块(类的数据成员):
- 字段(属性):字段是类里面声明的变量。字段表示对象的有关数据。
- 构造函数:类实例化时调用,可以为类的对象分配内存。
- 方法:方法为对象要执行的操作。
那么一个类的结构大致如下:
class 类名 {
// 字段
属性名: 类型;// 构造函数
constructor(参数: 类型) {
this.属性名 = 参数;
}// 方法
方法名() {
// 具体方法
}
}
示例代码:创建一个Car类
class Car { // 字段 engine: string; // 构造函数 constructor(engine: string) { this.engine = engine; } // 方法 disp(): void { console.log("发动机为:" + this.engine); } }
2.2 创建实例对象
在类中使用new
关键字来实例化类的对象,语法格式如下:
var 对象名 = new 类名([参数]);
类中的字段和方法可以使用.
来访问:
// 访问属性
对象名.字段名;// 访问方法
对象名.方法名();
示例代码:为Car类创建一个实例对象,并访问字段和方法
class Car { engine: string; constructor(engine: string) { this.engine = engine; } disp(): void { console.log("发动机为:" + this.engine); } } let bc = new Car("引擎1"); console.log(bc.engine); // 引擎1 bc.disp(); // 发动机为:引擎1
3 类的继承
TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。
类继承使用关键字extends
,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。
示例代码:
// 定义一个动物类Animal作为父类 class Animal { name: string; // 字段 constructor(name: string) { // 构造函数 this.name = name; } eat(): void { console.log(this.name + "在吃"); } } // 定义一个Cat类作为子类,继承父类Animal class Cat extends Animal { // 定义自己独有的方法叫 say() { console.log("喵喵喵"); } } // 创建Cat类的实例对象 let cat1 = new Cat("橘猫"); // 访问父类的eat()方法 cat1.eat(); // 橘猫在吃 // 访问自己的say()方法 cat1.say(); // 喵喵喵
继承类的方法重写(overwrite):又称为方法覆盖。类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。示例代码如下:
// 定义一个动物类Animal作为父类 class Animal { eat(): void { console.log("在吃饭"); } } // 定义一个Cat类作为子类,继承父类Animal class Cat extends Animal { // 对父类的方法进行重写 eat(): void { console.log("猫猫在吃鱼"); } } // 创建Cat类的实例对象 let cat1 = new Cat(); cat1.eat(); // 猫猫在吃鱼
在类中,使用super
关键字可以引用父类的属性和方法,super
就表示父类。例如,使用super
调用父类的普通方法:
// 定义一个动物类Animal作为父类 class Animal { eat(): void { console.log("在吃饭"); } } // 定义一个Cat类作为子类,继承父类Animal class Cat extends Animal { eat(): void { super.eat(); // 调用父类的eat()方法 } } // 创建Cat类的实例对象 let cat1 = new Cat(); cat1.eat(); // 在吃饭
使用super
调用父类的构造方法时,super
必须写在子类构造函数的第一条语句,如果父类的构造函数中有参数,那么在super
中也要将该参数传递进去。示例代码如下:
// 定义一个动物类Animal作为父类 class Animal { name: string; constructor(name: string) { this.name = name; } eat(): void { console.log("在吃饭"); } } // 定义一个Cat类作为子类,继承父类Animal class Cat extends Animal { age: number; constructor(name: string, age: number) { super(name); // 必须写在第一条语句 this.age = age; } }
4 static关键字
static
关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。
示例代码:
class StaticMem { static num: number; static disp(): void { console.log("num值为:" + StaticMem.num); } } StaticMem.num = 100; // 初始化静态变量 StaticMem.disp(); // 调用静态方法 // num值为:100
5 抽象类和抽象方法
用abstract
关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。abstract
抽象方法只能放在抽象类里面。抽象类和抽象方法用来定义标准。
抽象类不能用来创建对象,是专门用来被继承的类。
示例:定义一个抽象类和抽象方法
abstract class Animal { // 定义一个抽象类 name: string; constructor(name: string) { this.name = name; } abstract eat(): void; // 定义一个抽象方法 }
如果对抽象方法进行了具体的实现,那么就会报错:Method ‘eat’ Cannot have an implementation because it is marked abstract。
并且抽象类并不能被实例化,否则也会报错:Cannot create an instance of an abstract class。
如果某个类要继承抽象类,那么这个类要实现抽象类中定义的所有方法,否则也会报错。
例如,设计一个Cat继承Animal抽象类,如下所示,如果Cat类中什么方法都没有,就会出现提示,Cat类并没有实现抽象类中的抽象方法eat。
因此在继承时,在Cat类中必须实现eat方法:
abstract class Animal { // 定义一个抽象类 name: string; constructor(name: string) { this.name = name; } abstract eat(): void; } class Cat extends Animal { eat(): void { console.log(this.name + "吃鱼"); } }
6 类属性权限修饰符
6.1 public(公有的)
public
表示公有的、公开的,公有成员可以被任何地方访问,默认可见性
class Animal { public move() { // 使用public修饰符表示公开的成员 console.log("走"); } } var p = new Animal(); p.move(); // 走 class Cat extends Animal { bark() { console.log("喵喵喵"); } } var c = new Cat(); c.move(); // 走 c.bark(); // 喵喵喵
在类属性或方法前面添加public
关键字,来修饰该属性或方法是公有的。因为public
是默认可见性,所以可以直接省略。
6.2 protected(受保护的)
protected
表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。在类属性或方法前面添加protected
关键字,可以修饰属性或方法是受保护的。在子类的方法内部可以通过this来访问父类中受保护的成员,但是对实例不可见。
例如,在下面的代码中,move方法被protected修饰了,对Animal类创建实例,该实例并不能访问到move方法。
class Animal { protected move() { console.log("走"); } } var p = new Animal(); p.move(); // 不能访问到move方法
如果设计一个Cat类继承了Animal类,那么在Cat类的内部可以通过this访问到move方法。
class Animal { protected move() { console.log("走"); } } class Cat extends Animal { bark() { this.move(); } } var c = new Cat(); c.bark(); // 走
6.3 private(受保护的)
private
表示私有的,只在当前类中可见,对实例对象和子类都是不可见的。在类属性或方法前面添加private
关键字,来修饰该属性或方法是私有的。
例如,在Animal类中,move方法被private
修饰,因此实例对象a无法对move方法进行调用
class Animal { private move() { console.log("走"); } } var a = new Animal(); a.move(); // 报错
但是它可以在自己类内部的方法中进行调用:
class Animal { private move() { console.log("走"); } eat() { this.move(); // 使用正确不报错 } }
move方法同样也不能被子类所调用,对子类不可见:
class Animal { private move() { console.log("走"); } } class Cat extends Animal { bark() { this.move(); // 无法调用,报错 } }
6.4 readonly(只读)
readonly
表示只读,用来防止在构造函数之外对属性进行赋值。使用readonly
关键字只能修饰属性不能修饰方法。接口或者{}表示的对象类型,也可以使用readonly
。
例如,在下面的代码中,age被readonly修饰,是只读属性,因此在实例化时再对age进行修改就会报错。
class Person { readonly age: number = 18; } var p = new Person(); p.age = 19; // 报错,只读属性不能修改
如果不加readonly就可以修改:
class Person { age: number = 18; } var p = new Person(); p.age = 19; console.log(p.age); // 19
7 存取器
TypeScript支持通过getters/setters来截取对对象成员的访问。
在类中设置某个属性语法为:
set 属性名(参数): [类型]{
this.属性 = 参数;
}
获取某个属性语法为:
get 属性名(): [类型]{
return this.属性;
}
示例代码:
class Person { // 将tall属性设置为私有的,这样无法通过实例来修改 // 私有变量约定用下划线开头 private _tall: number; constructor(tall: number) { this._tall = tall; } // 获取tall的值 get height(): number { return this._tall; } // 设置tall的值 set height(tall: number) { this._tall = tall; } } var p = new Person(160); p.height = 180; console.log(p.height); // 180
到此这篇关于TypeScript面向对象超详细分析的文章就介绍到这了,更多相关TypeScript面向对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!