TypeScript中Interface接口的深度探索与实践

TypeScript 中的 class 是基于 ECMAScript 2015 (ES6) 引入的类语法的扩展。在 TypeScript 中,类是一种用于创建对象的蓝图或模板。它封装了对象的属性和方法,让代码更加模块化和可重用。

类的基本结构

cpp 复制代码
class MyClass {  
    // 类的属性(字段)  
    myProperty: string;  
    // 构造函数  
    constructor(initialProperty: string) {  
        this.myProperty = initialProperty;  
    }  
    // 类的方法  
    myMethod(): void {  
        console.log(this.myProperty);  
    }  
}  
// 创建 MyClass 的一个实例  
const myInstance = new MyClass("Hello, World!");  
// 调用实例的方法  
myInstance.myMethod(); // 输出: Hello, World!

这段代码展示了在TypeScript中如何定义一个类(MyClass),创建这个类的一个实例(myInstance),并调用该实例的一个方法(myMethod)。

  • 类的属性: myProperty 是一个字符串类型的属性,它用于存储类的实例的某个状态或数据。
  • 构造函数: constructor(initialProperty: string) 是一个特殊的方法,用于在创建类的实例时初始化对象。这里,它接受一个字符串参数 initialProperty,并将其值赋给类的 myProperty 属性。注意,在TypeScript中,即使你没有显式地定义构造函数,编译器也会为你生成一个空的构造函数。
  • 类的方法: myMethod() 是一个没有参数和返回值(返回类型为 void)的方法。它使用 console.log 输出 myProperty 属性的值。
  • 类的实例: 通过 new 关键字和类名 MyClass 创建了 MyClass 类的一个新实例,并将其赋值给常量 myInstance。在创建实例时,传递了字符串 "Hello, World!" 作为参数给构造函数,这个值被用于初始化 myInstance 实例的 myProperty 属性。
  • 实例的方法: 调用myInstance 实例的 myMethod 方法。由于 myMethod 方法内部使用 console.log 输出了 myProperty 属性的值,而 myProperty 属性已经被初始化为 "Hello, World!",因此控制台将显示输出 Hello, World!

this 关键字有一个特殊的含义,它指的是当前对象的上下文(context)。在类的方法中,当方法作为实例方法被调用时(即通过实例对象调用),this 会自动指向调用它的那个实例。

类的继承

在TypeScript中,类的继承是一个非常重要的概念,它允许你基于一个或多个已存在的类来创建新的类。继承使得代码的重用和扩展变得更加容易。

cpp 复制代码
class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}
class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}
const dog = new Dog();
dog.bark();
dog.move(10);

这个例子展示了最基本的继承:类从基类中继承了属性和方法。 这里, Dog是一个 派生类,它派生自 Animal 基类,通过 extends关键字。 派生类通常被称作 子类,基类通常被称作超类。 因为 Dog继承了 Animal的功能,因此我们可以创建一个 Dog的实例,它能够 bark()move()

cpp 复制代码
class Animal {
    name: string;
    constructor(theName: string) { 
    	this.name = theName; 
    }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { 
    	super(name); 
    }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    constructor(name: string) { 
    	super(name); 
    }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);

这个例子展示了一些上面没有提到的特性。 这一次,我们使用 extends关键字创建了 Animal的两个子类: HorseSnake。 与前一个例子的不同点是,派生类包含了一个构造函数,它必须调用 super(),它会执行基类的构造函数。 而且,在构造函数里访问 this的属性之前,我们一定要调用 super()。 这个是TypeScript强制执行的一条重要规则。

访问修饰符

public:公开的,谁都能用(默认public) private:当成员被标记成 private时,它就不能在声明它的类的外部访问。

cpp 复制代码
class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 错误: 'name' 是私有的.

protected:protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

cpp 复制代码
class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}
class Employee extends Person {
    private department: string;
    constructor(name: string, department: string) {
        super(name)
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误

注意,我们不能在 Person类外使用 name,但是我们仍然可以通过 Employee类的实例方法访问,因为 Employee是由 Person派生而来的。 构造函数也可以被标记成 protected。 这意味着这个类不能在包含它的类外被实例化,但是能被继承。

只读修饰符

只能读不能写,需要注意的时,即使是readonly的东西,在初始化之前是可以写,即在constructor中可以初始化或更改。

cpp 复制代码
class Person {
    readonly name: string;

    constructor(name: string) {
        this.name = name;
    }
}

const person = new Person("Alice");
person.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.

上面的代码可以更改为参数属性,将readonly属性与构造函数参数结合使用,你可以直接在构造函数参数上应用readonly修饰符,这样就不需要在构造函数体内显式地赋值了。

cpp 复制代码
class Person {
    constructor(public readonly name: string) {}
}

const person = new Person("Alice");
person.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.

存取器

TypeScript(以及它的基础JavaScript)中,存取器(Accessors)是一对特殊的方法,用于封装对对象属性的读取和写入操作。存取器由get和set关键字定义,分别用于获取(读取)和设置(写入)属性值。它们可以让你在不暴露实际数据成员的情况下控制对数据的访问。

cpp 复制代码
class MyClass {
    private _myValue: number;

    get myValue(): number {
        return this._myValue;
    }

    set myValue(value: number) {
        if (value >= 0) {
            this._myValue = value;
        } else {
            throw new Error('Value must be non-negative.');
        }
    }
}

在这个例子中,myValue存取器允许你读取和设置_myValue的值,但是设置时会检查值是否非负。这提供了额外的验证逻辑,而不会让外部代码直接访问或修改_myValue。 使用存取器 你可以像访问普通属性一样使用存取器:

cpp 复制代码
const obj = new MyClass();
obj.myValue = 10; // 设置值
console.log(obj.myValue); // 获取值,输出: 10

如果尝试设置一个非法值,将会抛出错误:

cpp 复制代码
obj.myValue = -5; // 抛出错误: Value must be non-negative.

静态属性

静态属性(Static Properties)是属于类本身的属性,而不是类的实例。这意味着无论创建多少个类的实例,静态属性都只有一个副本,存储在类的构造函数原型上,而不是在每个实例上。

cpp 复制代码
class MyClass {
    static myStaticProperty: string = "Hello, World!";
}
console.log(MyClass.myStaticProperty); // 输出: "Hello, World!"

class MyClass {
    static myStaticMethod() {
        return "Called static method.";
    }
}
console.log(MyClass.myStaticMethod()); // 输出: "Called static method."

静态属性和方法通常用于以下场景:

  • 常量或配置信息:如果你有一些常量或配置信息,这些信息对于所有实例都是相同的,那么使用静态属性会很有意义。
  • 实用工具方法:一些不依赖于实例状态的方法,可以作为静态方法提供,这样可以直接调用,无需创建实例。
  • 静态属性可以在类定义中初始化,也可以在类之外初始化:
cpp 复制代码
class MyClass {
    static myStaticProperty;
}

MyClass.myStaticProperty = "Initialized later";

console.log(MyClass.myStaticProperty); // 输出: "Initialized later"

抽象类

抽象类(Abstract Classes)是一种特殊的类,它不能被实例化,主要用于作为其他类的基础类。抽象类中可以包含抽象方法和抽象属性,这些抽象成员必须在继承自抽象类的子类中实现。

cpp 复制代码
abstract class Animal {
    abstract makeSound(): void; // 抽象方法,必须在子类中实现
    speak(): void {
        this.makeSound();
    }
}

class Dog extends Animal {
    makeSound(): void {
        console.log("Woof!");
    }
}

const dog = new Dog();
dog.speak(); // 输出: "Woof!"

注意事项

  • 抽象类不能被直接实例化。试图创建抽象类的实例会导致编译错误。
  • 抽象方法和抽象属性必须在所有非抽象的派生类中实现。
  • 抽象类可以包含非抽象方法和属性,这些可以被继承并在派生类中使用。
相关推荐
吕彬-前端11 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白32 分钟前
react hooks--useCallback
前端·react.js·前端框架
恩婧40 分钟前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
mez_Blog41 分钟前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript
珊珊而川1 小时前
【浏览器面试真题】sessionStorage和localStorage
前端·javascript·面试
森叶1 小时前
Electron 安装包 asar 解压定位问题实战
前端·javascript·electron
drebander1 小时前
ubuntu 安装 chrome 及 版本匹配的 chromedriver
前端·chrome
软件技术NINI1 小时前
html知识点框架
前端·html
深情废杨杨1 小时前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS1 小时前
【vue3】vue3.3新特性真香
前端·javascript·vue.js