四、面向对象
大约 6 分钟约 1653 字
(一)面向对象简介
- 程序中所有的操作都需要通过对象来完成。一切操作都要通过对象,即面向对象
- 操作浏览器 —— window 对象
- 操作网页 —— document 对象
- 操作控制台 —— console 对象
- 计算机程序的本质是对现实事物的抽象,抽象的反义词是具体
- 如:照片是对一个具体的人的抽象,汽车模型是对一辆具体的车的抽象
- 在程序中所有的对象都被分成了两个部分:数据 和 功能
- 如:人的姓名、性别、年龄、身高、体重等属于数据,人可以说话、走路、吃饭、睡觉等属于人的功能
- 数据在对象中被称为属性,功能被称为方法,在程序中一切皆是对象
(二)类(class)
- 要想面向对象、操作对象,首先要拥有对象,要创建对象,必须要先定义类
- 类可以理解为对象的模型,在程序中可以根据类创建指定类的对象
1.定义类
class 类名 {
属性名: 类型;
constructor(参数: 类型) {
this.属性名 = 参数;
}
方法名() {
......
}
}
/* =============== 示例 =============== */
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log("Hello, TS");
}
}
2.实例属性
- 直接定义的属性叫实例属性
- 需要通过对象的实例去访问
class Person {
// 定义实例属性
name: string = "Alice";
}
// 定义对象的实例
const person = new Person();
// 访问实例属性
console.log(person.name);
3.静态属性(类属性)
- 使用
static
定义的属性叫静态属性 - 可以直接通过类名去访问
class Person {
// 定义静态属性
static age: number = 20;
}
// 定义对象的实例
const person = new Person();
// 访问静态属性
console.log(Person.age);
4.只读属性
- 使用
readonly
定义的不可修改的属性叫只读属性 - 需要通过对象的实例去访问
class Person {
// 定义只读属性
readonly name: string = "Alice";
static readonly age: number = 20; // static应先声明
}
// 定义对象的实例
const person = new Person();
// 修改只读属性
person.name = "Tom"; // 报错
person.age = 30; // 报错
// 访问只读属性
console.log(person.name, person.age);
5.方法
- 直接声明的方法叫实例方法
- 使用
static
声明的方法叫静态方法(类方法)
class Person {
name: string = "Alice";
age: number = 20;
// 定义方法
sayHello() {
console.log("Hello, TS");
}
static sayHello2() {
console.log("Hello, TS");
}
}
// 定义对象的实例
const person = new Person();
// 调用方法
person.sayHello();
Person.sayHello2();
(三)构造函数和 this
constructor
被称为构造函数,会在对象创建时自动调用- 在构造方法中,当前对象就是当前
new
新建的对象,可以通过this
向新建对象添加属性 - 在实例方法中,
this
表示当前的实例,可以通过this
来表示当前调用方法的对象
class Dog {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
bark() {
alert("汪汪汪~");
console.log(this);
}
}
const dog1 = new Dog("旺财", 3);
const dog2 = new Dog("富贵", 5);
console.log(dog1, dog2);
dog1.bark();
dog2.bark();
(四)继承
- 使用继承后,子类将拥有父类中的所有方法
- 语法:
class 子类 extends 父类
- 可以将多个类中的共有代码写在一个父类中
- 子类中定义和父类相同的方法,则子类方法会覆盖父类方法 —— 重写
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log("动物在叫~");
}
}
class Dog extends Animal {
run() {
console.log(`${this.name}在跑`);
}
}
class Cat extends Animal {
sayHello() {
console.log("喵喵喵~");
}
}
const dog = new Dog("旺财", 5);
const cat = new Cat("富贵", 3);
console.log(dog);
console.log(cat);
dog.sayHello();
cat.sayHello();
dog.run();
(五)super 关键字
- 在子类的方法中,
super
就表示当前类的父类 - 如果子类声明了构造函数,则必须在所有
this
操作前调用父类的构造函数
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log("动物在叫~");
}
}
class Dog extends Animal {
age: number;
constructor(name: string, age: number) {
super(name);
this.age = age;
}
sayHello() {
super.sayHello();
}
}
const dog = new Dog("旺财", 5);
console.log(dog);
dog.sayHello();
(六)抽象类
- 以
abstract
定义的类叫抽象类 - 抽象类和其他类区别不大,只是不能用来创建对象,是专门用来被继承的类
- 抽象类中可以添加抽象方法
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
/*
定义一个抽象方法,只能定义在抽象类中
以abstract开头,且没有方法体
子类必须实现该方法
*/
abstract sayHello(): void;
}
class Dog extends Animal {
sayHello() {
console.log("汪汪汪~");
}
}
class Cat extends Animal {} // 报错
const dog = new Dog("旺财", 5);
dog.sayHello();
const animal = new Animal("狗"); // 报错
(七)接口
- 接口用来定义一个类(对象)结构,定义一个类中应该包含哪些属性
- 接口也可以当做类型声明使用
type
不能重复声明,interface
可以- 定义多个同名
interface
会把各属性作合并
- 接口可以在定义类时限制类的结构
- 接口中的所有属性都不能有实际的值
- 接口只定义对象的结构,而不考虑实际值
- 定义类时可以使类去实现一个接口
- 实现接口就是使类满足接口的要求
// 描述一个对象的类型
type myType = {
name: string;
age: number;
};
const obj: myType = {
name: "aaa",
age: 15,
};
interface myInterface {
name: string;
age: number;
}
const obj: myInterface = {
name: "aaa",
age: 15,
};
// 限制类结构
interface myInter {
name: string;
sayHello(): void;
}
// 实现接口
class MyClass implements myInter {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello(): void {
console.log("Hello");
}
}
(八)属性的封装
public
修饰的属性可以在任意位置访问(修改),默认值private
私有属性,只能在当前类内部进行访问(修改)- 通过在类中添加方法使得私有属性可以被外部访问
getter
方法用来读取属性,setter
方法用来设置属性,统称为属性的存取器
protected
受保护属性,只能在当前类和其子类中进行访问(修改)
class Person {
// 属性可以任意被修改,不安全
// name: string;
// age: number;
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// // 定义一个方法获取age属性
// getAge() {
// return this.age;
// }
// // 定义一个方法修改age属性
// setAge(val: string) {
// if (val >= 0) this.age = val;
// }
// TS中设置存取器
get age() {
return this.age;
}
set age(val: string) {
if (val >= 0) this.age = val;
}
}
const p = new Person("Alice", 20);
// p.name = "Tom";
// p.age = 30;
// console.log(p);
// getter & setter
// console.log(p.getAge());
// p.setAge(-33);
// TS get & set
console.log(p.age);
p.age = 30;
- 可以直接将属性定义在构造函数中【语法糖】
class A {
constructor(
public name: string,
private age: number,
) {}
}
const a = new A("Alice", 20);
console.log(a);