TypeScript----面向对象编程
类和对象
首先,在讲解类和对象之前,先举这样一个例子:
建造车:1.画图纸,定义车的特征以及车的功能。2.根据车的图纸来造车
创建对象:1.定义类,定义类的属性及方法。 2.根据定义的类来创造对象
//举个例子:
class Bench{
//颜色(属性)
color = 'white';
//最高速度(属性)
maxSpeed = 220;
//驾驶(方法)
dirve(){
console.log('run');
}
//鸣笛(方法)
honk = () =>{
console.log('滴~滴滴~-')
}
}
const bench = new Bench();
console.log(bench)
console.log(bench.color)
console.log(bench.maxSpeed)
bench.drive()
bench.honk()
通过一家造车厂可以批量生产出很多特征功能一样的汽车,而对象的创建也是如此,可以通过一个类创建多个相同的实物对象,这样的实物对象被称为实例对象
构造函数
构造函数是用来为对象赋予初始值或者执行初始化操作,构造函数名称只能使用constructor
class Bench{
constructor(){}
}
当使用new关键字创建对象时,可以传递参数,将想要赋予给对象的初始值传递到类的内部,而参数值一般就是对象中某个属性的初始值
class Bench{
constructor (color:string,maxSpeed:number){}
}
const bench = new Bench('red',220)
在构造函数中,this指向的时通过类创建出来的对象,所以可以通过this来点出对象中的属性,再对属性进行赋值。
class Bench{
color:string;
maxSpeed:number;
constructor(color:string,maxSpeed:number){
this.color = color;
this.maxSpeed = maxSpeed;
}
}
const bench = new Bench('red',220)
只读属性
只读属性,如其名,只读属性一旦被赋值,那么这个值就不能被更改。
可以再类中通过 readonly修饰符来讲类的属性设置为只读。
class Bench{
readonly color:string;
constructor(color:string){
this.color = color;
}
change(){
this.color = 'red'
}
}
const bench = new Bench('red')
//写到这里你会发现color的值不可被更改
继承
在面向对象编程中很多对象都会有相同的属性和方法,这样会导致程序中出现大量的重复性代码。
为此我们可以通过定义一个总类,然后将总类上有的属性和方法继承下去,这样程序中就不会出现大量的重复性代码了而且还可以再具体类中定义独有的属性和方法
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
在上述代码中,Animal被称为父类,而Dog和Cat被称为子类。
具体举例:
class Animal {
eat(){
console.log('eat')
}
}
class Cat extends Animal {
name = 'Tom'
}
class Dog extends Animal {
name = 'Kate'
}
let cat = new Cat();
cat.eat()
//eat
console.log(cat.name)
//Tom
let dog = new Dog()
dog.eat()
//eat
console.log(dog.name)
//kate
当子类继承了父类之后,若父类需要在初始化时传递参数,由子类接收,在子类的构造函数中通过super条约父类将参数传递给父类
class Animal{
constructor(public name :string){}
}
class Cat extends Animal {
old:number
constructor(old:number,name:string){
super(name);
this.old = old
}
}
const cat = new Cat(1,'Tom')
console.log(cat.old)
//1
console.log(cat.name)
//Tom
子类在继承父类之后,还可以通过重写父类方法对功能进行扩展:
class Animal{
run(){
console.log('run');
}
}
class Cat extends Animal{
override run(){
console.log("running");
super.walk();
}
}
const cat = new Cat();
cat.run()
//running
访问权限修饰符
通过访问权限修饰符可以只读类中的属性和方法,决定了它们能够在拿一些范围内被访问
修饰符 | 作用 |
---|---|
public | 被 public 关键字修饰的类属性和类方法可以在任何地方使用 (当前类、子类、实例对象) |
private | 被 private 关键字修饰的类属性和类方法只能在当前类中使用 |
protected | 被 protected 关键字修饰的类属性和类方法可以在当前类和子类中使用 |
- public 公开的 ( 可省略 )
class Animal{
constructor(){
this.run();
}
public run(){
console.log('run');
}
}
class Cat extends Animal{
run(){
super.run();
}
}
const cat = new Cat();
cat.run();
- private(私有的)
class Animal{
constructor(){
this.run()
}
private run(){
console.log('run');
}
}
class Cat extends Animal{
//在这里会飘红报错,因为run是私有属性,只能在Animal类中访问
run(){
super.run()
}
}
const animal = new Animal();
//这里也会报错,因为run是Animal的私有属性,只能在Animal类中访问
animal.run()
- protected 受保护的
class Animal{
constructor(){
this.run()
}
protected run(){
console.log('run');
}
}
class Cat extends Animal{
run(){
super.run();
}
}
const animal = new Aniaml();
//此处会飘红报错,因为run属性受保护,只能在Animal及其子类中访问
animal.dirve()
Getter \ Setter
Getter 和 Setter 是对属性的获取和设置进行封装,获取属性用Getter,修改属性值用Setter
class Animal{
private _salary:number;
constructor(salary:number){
this._salary = salaery;
}
get salary(){
return this._salary;
}
set salary(salary:number){
this._salary = salary;
}
}
const animal = new Animal(4000);
console.log(animal.salary);
//识别到获取数据,调用get
animal.salary = 6000;
//识别到更改数据,调用set
console.log(animal.salary);
//识别到获取数据,调用get
索引签名
在JavaScript中,可以为对象动态添加属性,但是在Typescript中这样是不被允许的,因为Typescript对于对象是有严格要求的类型限制
let person = {}
person.name = "张三";
//此时会报错,提示person对象中没用属性名name
那么如何在TypeScript中为对象动态添加属性呢?这就需要用到索引签名了,它可以在不确定属性名称的情况下限制属性的类型以及对象的类型
由于不确定属性名称,所以我们可以为对象动态添加任意符合要求类型的属性。
class Person{
[str:string]:string;
}
let person = new Person();
person.name1 = '张三';
person.name2 = '李四';
person[true] = 'hello'
//以上会飘红,因为true不能作为索引类型使用
person.name3 = 200;
//以上也会飘红报错,因为在Person中以及定义了字符串类型,所以这个属性不能为数字类型
person[0] = '王五';
静态成员
静态成员是指在类中被static关键字修饰的类属性和类方法,而静态成员属于类,所以可以用类名点出静态成员来进行访问
class Person{
static name : string = '张三';
}
Person.name;
//类名.静态成员
注意:!!!不能使用实例对象来点出静态成员!!!
用途: 一般来说,静态成员对于该类的任何一个对象而言,可以被当做一个公共的存储单元,该类的任何对象访问它时,不管是获取还是修改,都是在一个单元内进行操作,使用静态属性主要用于各个对象间的数据共享
class Person{
private static _count :number = 0;
contructor(){
Person._count++;
}
getCount(){
return Person._count;
}
}
const p1 = new Person();
const p2 = new Person();
console.log(p1.getCount());
console.log(p2.getCount());
要记住,静态成员一直存在于内存,而非静态成员要通过实例化才能分配到内存,使用静态成员不能访问非静态成员,但非静态成员可以访问类中的静态成员
class Person{
private static _count:number=0;
static fn(){
this.getCount
//这里会报错,因为静态成员不能访问非静态成员
//this指向的是类,而不是类实例
}
getCount(){
return Person._count
//非静态可以访问静态
}
}
抽象类
抽象类因继承而存在,通过抽象类可以约束子类必须实现那些成员,通俗来讲,抽象类就是为了继承而存在
在抽象类中可以只定义成员,具体的成员实现由子类完成且必须完成,使用抽象类不能被直接实例化
抽象类由abstract来定义
abstract class Animal {
abstract color : string;
abstract render():void;
}
class Cat extends Animal{
constructor(public color : string){
super();
}
override render():void{
console.log("render");
}
}
const cat = new Cat();
console.log(cat.color);
cat.render()
接口
接口,一般用于声明类型,用于对复杂的数据结构进行类型描述比如对象和函数以及类
接口的基本使用
接口声明需要使用interface关键字
使用接口约束对象的类型
interface User{
name:string;
age:number;
}
const user:User = {
name:'张三';
age:20;
};
使用接口约束函数类型
interface Sum{
(a:number.m:number):number;
}
const sun:Sum = function(a.b){
return a+b;
};
使用接口约束类的成员
interface Person {
name:string;
addEvent():void;
removeEvent():void;
}
class NewPerson implements Person{
name:string = 'test';
addEvent():void{}
removeEvent():void{
}
}
宽松的接口检查
TypeScript 接口检查时宽松的, 当变量满足了接口规范以后,几十遍了中存在接口范围以外的属性也是可以的。
interface User{
name:string;
age:number;
}
let user:User = {
name:'张三',
age:20,
};
let someone = {
name:'李四',
age:50,
sex:'男',
};
user = someone;
interface Reportable{
summary():void;
}
function printSummary(item:Reportable):void{
item.summary()
}
const person = {
name:'张三',
summary(){
console.log(`hello,my name is ${this.name}`);
},
};
printSummary(person);
对于宽松的接口检查政策字面量是个例外,也就是说对于字面量的接口类型检查还是严格的,不能出现接口以外的其他属性。
interface User {
name:string;
age:number;
}
const user : User = {name:'张三',age:20};
const another : User = {name:'李四',age:40,sex:'男'};
//此处会报错,因为接口内没有sex
interface Reportable{
summary():void;
}
function printSummary(item:Reportable):void{
item.summary();
}
printSummary({
name:'张三',
//此处会报错,name 不在类型Reportable中,对象字面量只能指定已知属性
summary(){
console.log(`hello,my name is ${this.name}`)
},
});
绕过严格类型检查模式:
- 类型断言
interface User{
name:string;
age:number;
}
const another : User = {name:'李四',age:40,sex:'男'} as User;
- 索引签名
interface User{
name:string;
age:number;
[key:string]:string | number;
}
const another : User = {name:'李四',age:40,sex:'男'}
接口继承
接口具有继承特性 接口与接口直接可以存在继承关系,而且一个接口可以继承多个接口
interface Sizes{
sizes:string[];
getSize():string[];
}
interface Shape{
color:string;
}
//Pizza 继承了两个接口,Sizes 以及 Shape
interface Pizza extends Sizes,Shap{
name:string;
}
let pizza:Pizza = {
name:'张三',
color:'skyblue',
sizes:['large','small'],
getSize(){
return this.sizes;
},
};
在继承了接口以后可以对被继承接口中的属性进行重写,但是重写的类型一定要在原有类型的范围以内。
interface User{
name:any;
age:number;
}
interface MyUser extends User{
name:bollean;
}
接口合并
接口具有声明合并特性 多个相同名称接口会自动合并
interface User {
height:number;
width:number;
}
interface User{
scale:number;
}
let user:User = {height:5,width:6,scale:10};
interface与 type
在TypeScript中,type可以定义对象、联合类型、基本数据类型,而interface只能定义对象类型。
// ISBN: 国际标准书号
type ISBN = number | string;
type PublicationT = {
isbn:ISBN;
another:string;
publisher:string;
}
interface PublicationI{
isbn:ISBN;
author:string;
publisher:string;
}