TypeScript——类(基本概念、类和对象语法、对象和内存、this、构造函数、静态方法、 存取器、继承、super、访问权限修饰符、static、重写、里氏替换原则)

目录

1)基本概念

2)类和对象语法

1、类的定义

2、类的属性

3、类的方法

3)对象和内存

4) this

5) 构造函数

6) 静态方法

7) 存取器

8) 继承

9) super

10) 访问权限修饰符

1、修饰符分类

2、字段或方法的权限修饰符

3、构造函数权限修饰符

4、访问修饰符与开闭原则

11)静态成员 static

1、概念: 

2、生命周期

3、静态成员的访问

4、静态方法中的this 

5、static解决不能创建构造函数为private和protected的问题

12、重写

13、里氏替换原则


ts是一个面向对象的语言,js是基于面向对象设计的脚本语言

1)基本概念

简单认知

  1. 类是现实世界或思维世界中的实体在计算机中的反映,它将数据(属性)以及这些数据上的操作(方法)封装在一起。

  2. 对象是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。

类与对象的关系

  1. 类是对象的抽象,而对象是类的具体实例,是通过new classname产生的。

  2. 类是抽象的,不占用内存,而对象是具体的,占用存储空间。

  3. 类是用于创建对象的蓝图(规划,抽象)。

专业术语

  1. 面向对象(OOP)的三大特性:封装、继承、多态;——面试题

    • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据;

    • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性;

    • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法;

  2. 存取器(getter & setter):用以改变属性的读取和赋值行为;

  3. 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法;

  4. 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现;

  5. 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口;

2)类和对象语法

1、类的定义

class Class_name {
    // 类体
}

1. 定义类的关键字为 class;

2. 类体中声明类的成员(都是可选的)

- 字段 −  也就是属性,可包含0到多个
- 构造函数 −  constructor,可包含0到1个
- 方法− 也就是行为动作,可包含0到多个

2、类的属性/字段

(1)声明属性时加类型声明是可选的;

(2)声明字段时初始化值是可选的;

3、类的方法

类的方法参数和返回值都可声明类型,但都是可选的;

严格模式下必须要声明类型

class Car {
    // 属性  字段
    engine:string; //没有初始化
	price:number = 15;//初始化值为15
  
    //方法
	disp():void {
		console.log(this.engine, this.price);
	}
	toGo(len:number):boolean {
		console.log(`走了${len}米远`);
		return true;
	}
  
	//字段式的方法,本质上这不是方法,而是字段
	m1 = (a:number, b:number):void=>{
		console.log("m1()")
	}
	m2:(a:number,b:number)=>void = (a, b)=>{
		console.log("m1()",a+b)
	}
	m3 = function():void {
		console.log("m3()")
	}
}

let car:Car = new Car();
console.log(car.engine);
console.log(car.price);
car.disp();
car.toGo(100);
car.m1(1,2);
car.m2(11,22);
car.m3();

3)对象和内存

1.  new多次本质上是创建了多个了对象, 只有创建了对象,才能使用对象访问对象的字段或方法;
2.  对象在堆中,计算机堆中内存无名字,只能通过地址使用之,堆中对象地址通过hash后得到引用值,引用类型变量保存的就是引用值;
3.  每个对象都有唯一的内存地址, 只能通过对象的引用使用对象, new 会返回对象的引用(this);
4.  当类中定义了属性,该类的每个对象都会独立开辟出属性成员,当前对象独享;
5.  创建对象时,方法不会被开辟出来,同一个类的方法被所有该类对象共用(原型和原型链);
6.  同一对象可被多个变量引用,当一个对象没有引用时,对象的生命就被GC(拉圾回收机制)管理了, 当对象销毁时,对象中独享的字段成员也会随之销毁;
7.  只有类不能做事情,类的对象才有用, 类成员(静态成员例外);

4) this

方法中可以使用this,该this引用调用方法的对象, 所以方法里可以使用 `this.方法`和`this.字段`访问实例成员;

5) 构造函数

1. 函数名为 constructor;

2. 可带0到多个参数 (构造函数没有返值,也不用声明返回值类型);

3. 实例化对象;

   3.1 构造函数无参时

        new 类名();

   3.2 构造函数有参时

   ​    new 类名(为构造函数传实参)

4. 实例化过程(new 的过程发生了什么);

   ​    (1)在堆中实例化对象, 包括创建字段成员

   ​    (2)执行构造函数

   ​    (3)返回对象引用

5. 构造函数存在的价值

​      (1)初始化一些操作并为对象属性赋值;

​      (2)构造函数中this值为新实例化的对象引用, 常通过"this.字段=值"为新对象的字段赋值;

class Car {
    engine:string; 
	price:numbe;
	constructor(engine:string, price:number) {  //形参
		this.engine = engine;
		this.price = price;
	}
    printf():void {
	   console.log(`engin=${this.engine}, price=${this.price}`);
    }
}
let car = new Car("德国进口", 30);
car.printf();

6) 静态方法

区别:对象调用还是类调用(静态方法是类调用)

class Person{
	tool():number{
		console.log(111)
		return 100
	}
	static tool2(n1:number):number{
		return n1*n1
	}
}
Person.tool2(100)
let p1:Person=new Person()
p1.tool()
class Person{
	//字段--未来创建出的对象的属性
	n:number=100
	static n2:number=90
	static fn():void{console.log(666)}
}
let a:number=Person.n2  //可以用类名来访问
console.log(a)
let p1:Person=new Person()
console.log(p1.n)  //p1只能访问n,因为其它两个是类的方法
Person.fn()

7) 存取器

存取器:用户提交的任何数据都是不可信的  CSRF
使用 getter 和 setter 可以改变属性的赋值和读取行为,并且更安全(用户提交数据的时候提交的是攻击代码):做一些自己想做的事情,比如数据发生改变的时候,重新渲染页面

class Animal {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return 'Jack';
  }
  set name(value) {
    console.log('setter: ' + value);
  }
}

let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

class Student {
	birth: string
	constructor(birth) {
		this.birth = birth
	}
	get age() {
		return new Date().getFullYear() - new Date(this.birth).getFullYear()
	}
	set age(v) {
		this.birth = new Date(new Date().getFullYear() - v, 0, 0).toLocaleDateString()
	}
}
let p1: Student = new Student("1997-02-03")
let a = p1.age  //取值,得到get算出来的结果
p1.age = 21  //会执行set
console.log(p1.age)

8) 继承

1. 语法

class child_class_name extends parent_class_name { }

2. 只能单继承(多继承会违背类本质), 一个类的父类只能有一个,但是一个类可以有多个子类。子类也可以再次被当作父类,形成树结构;

3. 子类继承父类后,不会影响父类的独立使用;

4. 类型上包含关系才能使用继承(同类事物才能使用继承, 如人是动物,所以人可以继承动物);

5. 类中只能写本质的东西,如一个表示人的类,不能在类中写打乒乓球的方法,一个表示动物的类中不能写在水里游的方法。否则父类会侵略子类;

6. 子类对象可以访问从直接和间接父类中继承下来的成员;

7. 继承最原始的作用是什么?就是为了代码重用

class A{
	a:number=100
}
class B{
	b:number=200
}
class C extends A{
}
class D extends B{
}
// 错误:一个类不能继承多个
class E extends A,B{	
}
let e=new E()



class Animal {
    public name;
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is ${this.name}`;
    }
}

let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack


//类的继承
使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。
class Cat extends Animal {
  constructor(name) {
    super(name); // 调用父类的 constructor(name)
    console.log(this.name);
  }
  sayHi() {
    return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
  }
}

let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom

9) super

  1. 子类并且该子类带有构造方法,则该子类构造函数必须使用super(<实参>),不管父类是否写了构造方法;

  2. super要位于构造方法中的this使用之前;

  3. 子类对象的创建过程:创建父类对象,然后在父类对象基础上追加子类成员得到子类对象,然后子类构造函数先执行,子类构造函数中super(<实参>)再调用父类构造函数的执行 ;

  4. 没有继承父类,构造函数中就不能有super(<实参>);

class Shape { 
   area:number = 10;
   constructor() {
	   console.log("Shape")
   } 
} 
class Circle extends Shape { 
	length:number = 300;
	constructor() {
		super();//父类中没有构造方法也得加这该代码
		console.log("Circle");
	} 
   disp():void { 
      console.log(this.area, this.length); 
   } 
}
var obj = new Circle(); 
obj.disp()

5. super(父类所需参数)

   如果父类和子类都有构造函数,并且父类构造函数有参数,那么要求子类构造函数 super(...)中必须为父类传参数。

6. new 创建对象时,如果类中有构造方法,则以该构造方法所需参数传参

class Shape {
   area:number = 10;
   constructor(area) {
		console.log("Shape");
		this.area = area;
   }
}
class Circle extends Shape {
   length:number = 300;
   constructor(area, length) {
	   super(area);//area为父类构造函数传参数
	   console.log("Circle");
	   this.length = length;
   } 
   disp():void {
      console.log(this.area, this.length); 
   }
}
var obj = new Circle(10, 20); //以Circle构造函数所需参数传参
obj.disp()

7.new 对象时,如果类中无构造函数,并且该类又有父类,则以父类构造方法所需参数传参。(不提倡使用)

class Shape { 
   area:number = 10;
   constructor(area:number) {
        this.area = area;
			  console.log("Shape");
   } 
} 
class Circle extends Shape { 
    length:number = 300;
    disp():void { 
		    console.log("==>",this.area, this.length); 
    } 
}
var obj = new Circle(20); //以父类Shape所需参数传参
obj.disp()

8.构造函数中的super还可以访问父类中继承下来的成员.(如果没有重写,不提倡使用)

实际上同一个类中super和this可以出现在构造函数和方法中,它们的值都是一样的,但是它们的偏移量不相同,super只管到父类对象,而this管到子类对象,而父类对象只是子类对象的一个组成部分。所以super只能访问父类的成员,而this可以访问父类和子类的成员

interface mytype{
	data:object
}
class Vue{
	data:object
	constructor(obj:mytype){
		this.data=obj.data
	}
}
let vm=new Vue({data:{mgs:"hello"}})
console.log(vm.data)

class VueComponent extends Vue{
	constructor(arg:mytype){
		super(arg)
		//相当于new Vue()  就是继承的意义  
		//就是让VueComponent创建的对象有VueComponent类提供属性和方法还有父类提供的属性和方法
	}
	name="helo"
}
let box:VueComponent=new VueComponent({data:{msg:"66666"}})
console.log(box.data,box.name)

class KeepAlive extends Vue{
	constructor(arg:mytype){
		super(arg)
	}
	active:()=>void=function(){
		
	}
}
var c1=new KeepAlive({data:{}})
console.log(c1.data)
new Vue({data:{}})

10) 访问权限修饰符——面试题

1、修饰符分类

public:公用 

private:私有 

protected:  受保护 

2、字段或方法的权限修饰符

private修饰   的字段和方法只能在当前类中被使用

protected修饰   的字段和方法只能在当前类及其子类中被使用

public 修饰   的字段和方法在任何地方都可被使用, 

(如果没有加权限修饰符,则默认为public)

对象中能访问的,一般类里面能访问。类里面能访问的,对象不一定能访问。

3、构造函数权限修饰符

private修饰构造函数,“new 类(...)” 只能在当前类的方法中进行, 构造函数为private的类不能当作父类

protected修饰构造函数, “new 类(...)” 只能在当前类及其子类中进行

public修饰构造函数, "new 类(...)" 任何地方可进行

4、访问修饰符与开闭原则

(1) 类中成员使用什么修饰符修饰,思想上必须符合开闭原则.

(2) 成员量写成私有,为成员变量提供get/set方法, 之所以这样做是因为函数安全性大于字段安全性

class Person {
	private name: string;
	private age: number;
    
	public getName() {
		return this.name;
	}
	public setName(name) {
		this.name = name;
	}
}

let p = new Person();
p.setName("小王");  //p.name = "小王"

(3)一个类中有一个功能,可以会分成多个步骤,可以把这些步骤使用private修饰方法进行封装,这样可以把实现细节隐藏(重要),还可以代码重用(次要)

class Person { 
	//脱衣服
	private disrobe() {
		//.....
	}
	// 穿衣服
	private dress() {
		//.....
	}
	//洗操
	public takeAbath() {
		this.disrobe();
		console.log("洗澡了");
		this.dress();
	} 
	//睡觉
	public sleep() {
		this.disrobe();
		console.log("睡觉了");
		this.dress();
	}
} 

11)静态成员 static

1、概念: 

     类中使用static修饰的字段或方法,被修饰的成员称为静态成员,也称为类成员

2、生命周期

(1)静态成员也叫类成员,不创建对象时就存在了,对象的创建与销毁不会影响类成员的创建与销毁

(2)天生永恒的存在, 因为类成员所在内存是"数据段",而计算机数据段中的内存是不会被销毁的

3、静态成员的访问

      类名.成员 

4、静态方法中的this 

​     ( 就是ES5中的函数对象 )

-         非静态方法中"this"表示当前类的对象(new 当前类())
-         静态方法中"this"表示当前类

5、static解决不能创建构造函数为private和protected的问题

(1)构造函数使用private修饰,该类不能当作父类, 并且也只能在当前类中 "new..."的操作

(2)怎样得到private修饰构造函数的类的对象,使用static

class MyObj {
	private constructor() {
	}
	public m1() {
		console.log("m1()")
	}
	static public getMyObj() {
		return new MyObj();
	}
}

let obj1 = MyObj.getMyObj();
obj1.m1();

12、重写

1 方法可以被重写。字段被重写无意义,不要做这种操作。

2 重写的语法:

​    在父类和子类中, 两个方法名相同, 返回值类型相同, 参数相同, 子类重写方法时不能使用比父类中被重写的方法更加严格的修饰符

3 不能重写私有的方法 

4 重写的思想

  所有子类都需要的方法,但是有些子类对方法不满意

5 使用子类对象访问有重写的方法时,父类方法会被隐藏,如果要访问父类中被重写的方法, 子类的构造函数或子类的方法中使用super来访问

6 实战中,super只有两种情况有实战意义

(1) 子类重写的方法里面使用super调用父类中被重写的方法

(2) 子类构造函数使用super调用父类构造函数

13、里氏替换原则

​    里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。
里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
里氏替换原则指导下要求父类的私有的成员,也必须有公用的方法间接你传递给子类。

猜你喜欢

转载自blog.csdn.net/qq_52301431/article/details/127021021