Typescript的类的相关知识及进阶教程

Typescript的类的相关知识及进阶教程

1. 类的基本定义

类的定义使用 class 关键字,可以包含属性和方法。

class MathUtils {
  static PI: number = 3.14;

  static calculateCircumference(radius: number): number {
    return 2 * MathUtils.PI * radius;
  }
}

console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.calculateCircumference(5)); // 输出:31.4

在这个例子中,我们定义了一个 Person 类,它包含了一个 name 属性和一个 sayHello 方法。我们通过构造函数来初始化 name 属性,并使用 new 关键字创建了一个 Person 对象,并调用了它的 sayHello 方法。

2. 类的继承

类可以通过继承来扩展现有的类,并继承父类的属性和方法。

class Animal {
  name: string;

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

  eat() {
    console.log(`${this.name} is eating.`);
  }
}

class Cat extends Animal {
  breed: string;

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

  meow() {
    console.log(`${this.name} (${this.breed}) says meow.`);
  }
}

let cat = new Cat("Kitty", "Persian");
cat.eat(); // 输出:Kitty is eating.
cat.meow(); // 输出:Kitty (Persian) says meow.

在这个例子中,我们定义了一个 Animal 类,它包含了一个 name 属性和一个 eat 方法。然后,我们定义了一个 Cat 类,它继承自 Animal 类,并添加了一个 breed 属性和一个 meow 方法。我们通过调用父类的构造函数 super(name) 来初始化继承自父类的属性,并使用 new 关键字创建了一个 Cat 对象,并调用了它的继承自父类的 eat 方法和自己的 meow 方法。

3. 类的访问修饰符

类的属性和方法可以使用访问修饰符来控制其可访问性。

  • public:默认的修饰符,表示属性和方法是公共的,可以在类的内部和外部访问。
  • private:表示属性和方法是私有的,只能在类的内部访问。
  • protected:表示属性和方法是受保护的,可以在类的内部和派生类中访问。
class Person {
  private name: string;
  protected age: number;

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

  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  }

  getAge() {
    console.log(`I am ${this.age} years old.`);
  }
}

class Student extends Person {
  school: string;

  constructor(name: string, age: number, school: string) {
    super(name, age);
    this.school = school;
  }

  study() {
    console.log(`${this.name} is studying at ${this.school}.`);
  }
}

let student = new Student("Alice", 18, "XYZ School");
student.sayHello(); // 错误:name 是私有属性,无法在类的外部访问
student.getAge(); // 输出:I am 18 years old.
student.study(); // 输出:Alice is studying at XYZ School.

在这个例子中,我们定义了一个 Person 类,它包含了一个私有的 name 属性和一个受保护的 age 属性。然后,我们定义了一个 Student 类,它继承自 Person 类,并添加了一个 school 属性和一个 study 方法。私有属性 name 只能在类的内部访问,受保护属性 age 可以在类的内部和派生类中访问。我们通过调用父类的构造函数 super(name, age) 来初始化继承自父类的属性,并使用 new 关键字创建了一个 Student 对象,并调用了它继承自父类的 getAge 方法和自己的 study 方法。

4. 类的静态属性和方法

类的静态属性和方法是属于类本身而不是类的实例,可以直接通过类名访问。

class MathUtils {
  static PI: number = 3.14;

  static calculateCircumference(radius: number): number {
    return 2 * MathUtils.PI * radius;
  }
}

console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.calculateCircumference(5)); // 输出:31.4

在这个例子中,我们定义了一个 MathUtils 类,它包含了一个静态属性 PI 和一个静态方法 calculateCircumference。我们可以直接通过类名访问静态属性和方法,而无需创建类的实例。

5. 抽象类(Abstract Class)

抽象类是一种不能被直接实例化的类,它只能被继承。抽象类可以包含抽象方法,这些方法只有方法的签名,没有具体的实现,需要在派生类中实现。

abstract class Animal {
  abstract makeSound(): void;
  move(): void {
    console.log("Moving...");
  }
}

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

let cat = new Cat();
cat.makeSound(); // 输出:Meow!
cat.move(); // 输出:Moving...

在这个例子中,我们定义了一个抽象类 Animal,它包含了一个抽象方法 makeSound 和一个具体的方法 move。抽象方法 makeSound 在抽象类中只有方法的签名,并没有具体的实现,需要在派生类中实现。我们创建了一个 Cat 类,并实现了抽象方法 makeSound,同时继承了抽象类中的具体方法 move。通过创建 Cat 的实例,我们可以调用抽象方法和具体方法。

6. 接口与类的结合

接口可以用于描述类的形状,也可以用于约束类的行为。一个类可以实现多个接口,并且接口也可以继承其他接口。

interface CanFly {
  fly(): void;
}

interface CanSwim {
  swim(): void;
}

class Bird implements CanFly {
  fly(): void {
    console.log("Flying...");
  }
}

class Fish implements CanSwim {
  swim(): void {
    console.log("Swimming...");
  }
}

class Duck implements CanFly, CanSwim {
  fly(): void {
    console.log("Flying...");
  }

  swim(): void {
    console.log("Swimming...");
  }
}

let bird = new Bird();
bird.fly(); // 输出:Flying...

let fish = new Fish();
fish.swim(); // 输出:Swimming...

let duck = new Duck();
duck.fly(); // 输出:Flying...
duck.swim(); // 输出:Swimming...

在这个例子中,我们定义了两个接口 CanFlyCanSwim,分别描述了能够飞行和游泳的行为。然后,我们创建了 Bird 类和 Fish 类,它们分别实现了 CanFlyCanSwim 接口的要求。我们还创建了 Duck 类,它同时实现了 CanFlyCanSwim 接口。通过创建不同类的实例,我们可以调用相应接口中定义的方法。

7. 类的 Mixin

Mixin 是一种通过组合多个类的特性来创建新类的方式。在 TypeScript 中,我们可以通过将多个类作为父类混合在一起来创建新的类。

class Jumpable {
  jump(): void {
    console.log("Jumping...");
  }
}

class Swimmable {
  swim(): void {
    console.log("Swimming...");
  }
}

class FlyingFish implements Jumpable, Swimmable {
  jump(): void {
    console.log("Jumping...");
  }

  swim(): void {
    console.log("Swimming...");
  }
}

let flyingFish = new FlyingFish();
flyingFish.jump(); // 输出:Jumping...
flyingFish.swim(); // 输出:Swimming...

在这个例子中,我们利用 Typescript 中implements 对类的多继承性来实现 mixin。我们定义了两个类 JumpableSwimmable,它们分别表示可以跳跃和游泳的特性。然后,我们创建了 FlyingFish 类,并混合了 JumpableSwimmable 类。通过创建 FlyingFish 的实例,我们可以调用混合类中的方法。

mixin 的缺点

  • mixin 导致的隐式依赖 (Mixins introduce implicit dependencies)
  • mixin 导致命名冲突 (Mixins cause name clashes)
  • mixin 滥用可能会导致代码复杂度直线上升

8. 类的装饰器(Class Decorators)

装饰器是一种特殊类型的声明,可以附加到类声明、方法、属性或参数上,用来修改类的行为。

function Logger(target: Function) {
  console.log("Logging...");
}

@Logger
class Person {
  name: string;

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

let person = new Person("Alice");

在这个例子中,我们定义了一个装饰器函数 Logger,它接受一个参数 target,表示被装饰的类。在装饰器函数中,我们可以执行一些操作,比如打印日志。然后,我们使用 @Logger 将装饰器应用到 Person 类上。当创建 Person 类的实例时,装饰器函数会被调用并执行相应操作。

9. 类型别名与类的结合

类型别名可以用于定义类的类型,使得代码更加可读和可维护。

type Point = {
  x: number;
  y: number;
};

class Rectangle {
  position: Point;
  width: number;
  height: number;

  constructor(position: Point, width: number, height: number) {
    this.position = position;
    this.width = width;
    this.height = height;
  }
}

let rect: Rectangle = {
  position: { x: 0, y: 0 },
  width: 100,
  height: 50
};

在这个例子中,我们使用类型别名 Point 定义了一个包含 xy 属性的对象类型。然后,我们定义了一个 Rectangle 类,它包含了一个 position 属性(类型为 Point)和 widthheight 属性。我们创建了一个符合 Rectangle 类型要求的对象,并赋值给变量 rect

10. 泛型与类的结合

泛型可以用于类的成员、方法或整个类,以便在使用时指定类型参数,增加代码的灵活性和重用性。

class Box<T> {
  private contents: T[];

  constructor() {
    this.contents = [];
  }

  add(item: T) {
    this.contents.push(item);
  }

  getContents(): T[] {
    return this.contents;
  }
}

let stringBox = new Box<string>();
stringBox.add("Apple");
stringBox.add("Banana");
console.log(stringBox.getContents()); // 输出:["Apple", "Banana"]

let numberBox = new Box<number>();
numberBox.add(1);
numberBox.add(2);
console.log(numberBox.getContents()); // 输出:[1, 2]

在这个例子中,我们定义了一个泛型类 Box<T>,它包含了一个私有的 contents 数组,用于存储类型为 T 的项目。我们在类的实例化时可以指定具体的类型参数。通过调用 add 方法将项目添加到 contents 数组中,通过调用 getContents 方法获取 contents 数组。我们创建了一个 Box 实例,使用不同的类型参数(stringnumber),并添加了相应类型的项目。

11. 静态成员

类的静态成员是属于类本身的,而不是类的实例。静态成员可以通过类名直接访问,无需创建类的实例。

class MathUtils {
  static PI: number = 3.14;

  static calculateCircumference(radius: number): number {
    return 2 * MathUtils.PI * radius;
  }
}

console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.calculateCircumference(5)); // 输出:31.4

在这个例子中,我们定义了一个 MathUtils 类,它包含了一个静态属性 PI 和一个静态方法 calculateCircumference。我们可以直接通过类名访问静态属性和方法,而无需创建类的实例。

12. 访问器/存取器(Accessors)

访问器是一种用于封装类的成员访问和赋值行为的方式。通过定义 getset 方法,我们可以控制对类成员的访问和赋值。

class Person {
  private _name: string;

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }
}

let person = new Person();
person.name = "Alice";
console.log(person.name); // 输出:Alice

在这个例子中,我们定义了一个 Person 类,它包含了一个私有属性 _name 和一个公共访问器 name。通过定义 getset 方法,我们可以在访问和赋值时执行相应的逻辑。通过创建 Person 的实例,并通过访问器进行赋值和访问,我们可以控制对 _name 属性的操作。

13. 类的方法重载

方法重载是指在一个类中定义多个同名的方法,但它们具有不同的参数类型或参数个数。在调用方法时,编译器会根据参数的类型和个数来确定要调用的方法。

class Calculator {
  add(a: number, b: number): number;
  add(a: string, b: string): string;
  add(a: any, b: any): any {
    return a + b;
  }
}

let calculator = new Calculator();
console.log(calculator.add(1, 2)); // 输出:3
console.log(calculator.add("Hello", "World")); // 输出:HelloWorld

在这个例子中,我们定义了一个 Calculator 类,它包含了两个同名的 add 方法,分别接受两个 number 类型的参数和两个 string 类型的参数。根据参数的类型,编译器会在调用时选择相应的方法。

14. 类的扩展与重写

在 TypeScript 中,子类可以继承父类的属性和方法,并且可以重写父类的方法。

class Animal {
  protected name: string;

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

  move(distance: number): void {
    console.log(`${this.name} is moving ${distance} meters.`);
  }
}

class Cat extends Animal {
  move(distance: number): void {
    console.log(`${this.name} is sneaking ${distance} meters.`);
  }
}

let cat = new Cat("Kitty");
cat.move(10); // 输出:Kitty is sneaking 10 meters.

在这个例子中,我们定义了一个 Animal 类,它包含了一个受保护的 name 属性和一个 move 方法。然后,我们创建了一个 Cat 类,它继承自 Animal 类,并重写了 move 方法。通过创建 Cat 的实例,我们可以调用重写后的 move 方法。

15. 抽象类与抽象方法

抽象类是一种不能被直接实例化的类,它只能被继承。抽象类可以包含抽象方法,这些方法只有方法的签名,没有具体的实现,需要在派生类中实现。

abstract class Animal {
  abstract makeSound(): void;
  move(distance: number): void {
    console.log(`Moving ${distance} meters.`);
  }
}

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

let cat = new Cat();
cat.makeSound(); // 输出:Meow!
cat.move(10); // 输出:Moving 10 meters.

在这个例子中,我们定义了一个抽象类 Animal,它包含了一个抽象方法 makeSound 和一个具体的方法 move。抽象方法 makeSound 在抽象类中只有方法的签名,并没有具体的实现,需要在派生类中实现。我们创建了一个 Cat 类,并实现了抽象方法 makeSound,同时继承了抽象类中的具体方法 move。通过创建 Cat 的实例,我们可以调用抽象方法和具体方法。

16. 类型保护与类的使用

在 TypeScript 中,我们可以使用类型保护来判断一个对象是否属于某个类的实例,以便在代码中进行相应的操作。

class Car {
  drive(): void {
    console.log("Driving the car.");
  }
}

class Bicycle {
  ride(): void {
    console.log("Riding the bicycle.");
  }
}

function move(vehicle: Car | Bicycle): void {
  if (vehicle instanceof Car) {
    vehicle.drive();
  } else if (vehicle instanceof Bicycle) {
    vehicle.ride();
  }
}

let car = new Car();
let bicycle = new Bicycle();

move(car); // 输出:Driving the car.
move(bicycle); // 输出:Riding the bicycle.

在这个例子中,我们定义了一个 Car 类和一个 Bicycle 类,它们分别表示汽车和自行车。然后,我们定义了一个 move 函数,它接受一个 CarBicycle 类型的参数 vehicle。在函数内部,我们使用 instanceof 运算符来判断 vehicle 的类型,并调用相应类型的方法。通过传入不同类型的对象,我们可以在 move 函数中根据对象的类型执行相应的操作。

17. 类型推断与类的使用

TypeScript 是一种静态类型语言,它可以根据上下文自动推断变量的类型。在使用类时,我们可以利用类型推断来简化代码。

class Person {
  constructor(private name: string) {}

  sayHello(): void {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

let person = new Person("Alice");
person.sayHello(); // 输出:Hello, my name is Alice.

在这个例子中,我们定义了一个 Person 类,它包含了一个私有属性 name 和一个 sayHello 方法。在构造函数中,我们使用 private 关键字将参数 name 声明为私有属性。在创建 Person 类的实例时,我们不需要显式地声明变量的类型,TypeScript 可以根据赋值的类型自动推断出变量的类型。通过调用实例的方法,我们可以输出相应的结果。

18. 类的静态成员与命名空间

类的静态成员是属于类本身的,而不是类的实例。静态成员可以通过类名直接访问,无需创建类的实例。为了组织和管理类的静态成员,我们可以使用命名空间。

namespace MathUtils {
  export const PI: number = 3.14;

  export function calculateCircumference(radius: number): number {
    return 2 * PI * radius;
  }
}

console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.calculateCircumference(5)); // 输出:31.4

在这个例子中,我们使用命名空间 MathUtils 来管理数学相关的静态成员。通过使用 export 关键字,我们将 PIcalculateCircumference 导出为命名空间的公共成员。在使用时,我们可以通过命名空间和成员名来访问静态成员。

19. 类的模块化与导入导出

为了使类在不同的文件中可以使用和管理,我们可以将类定义放在单独的模块中,并通过导入和导出来使用这些模块。

// mathUtils.ts
export class MathUtils {
  static PI: number = 3.14;

  static calculateCircumference(radius: number): number {
    return 2 * MathUtils.PI * radius;
  }
}

// main.ts
import { MathUtils } from './mathUtils';

console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.calculateCircumference(5)); // 输出:31.4

在这个例子中,我们将 MathUtils 类定义放在一个名为 mathUtils.ts 的模块中,并使用 export 关键字将其导出。在另一个文件 main.ts 中,我们使用 import 关键字来导入 MathUtils 类,并使用它的静态成员。

20. 类型别名与类的结合

类型别名可以用于定义类的类型,使得代码更加可读和可维护。

type Point = {
  x: number;
  y: number;
};

class Rectangle {
  position: Point;
  width: number;
  height: number;

  constructor(position: Point, width: number, height: number) {
    this.position = position;
    this.width = width;
    this.height = height;
  }
}

let rect: Rectangle = {
  position: { x: 0, y: 0 },
  width: 100,
  height: 50
};

在这个例子中,我们使用类型别名 Point 定义了一个包含 xy 属性的对象类型。然后,我们定义了一个 Rectangle 类,它包含了一个 position 属性(类型为 Point)和 widthheight 属性。我们创建了一个符合 Rectangle 类型要求的对象,并赋值给变量 rect

21. 类的参数属性

TypeScript 提供了一种简化类成员初始化的语法,称为参数属性。使用参数属性,我们可以在构造函数的参数前面加上访问修饰符,从而自动将参数声明为类的属性,并进行初始化。

class Person {
  constructor(public name: string, private age: number) {}

  sayHello(): void {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

let person = new Person("Alice", 20);
person.sayHello(); // 输出:Hello, my name is Alice and I'm 20 years old.

在这个例子中,我们使用参数属性来简化 Person 类的构造函数。通过在构造函数的参数前面加上访问修饰符,我们不仅将参数声明为类的属性,还可以直接进行初始化。这样,在创建 Person 类的实例时,我们可以直接传递参数,并访问属性。

22. 类的静态成员与类的实例

类的静态成员是属于类本身的,而不是类的实例。静态成员可以通过类名直接访问,无需创建类的实例。在类的静态成员中,无法访问类的实例属性或方法,只能访问静态属性或方法。

class Person {
  static country: string = "Earth";

  name: string;

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

  static sayHello(): void {
    console.log(`Hello from ${Person.country}!`);
  }
}

let person = new Person("Alice");
console.log(person.name); // 输出:Alice
console.log(Person.country); // 输出:Earth
Person.sayHello(); // 输出:Hello from Earth!

在这个例子中,我们定义了一个 Person 类,它包含了一个静态属性 country 和一个实例属性 name。我们创建了一个 Person 类的实例,并访问实例属性 name。同时,我们直接通过类名访问静态属性 country 和静态方法 sayHello

23. 类的可选属性与默认值

在 TypeScript 中,我们可以给类的属性指定可选属性或默认值,以增加灵活性和便利性。

class Person {
  name: string;
  age?: number;
  gender: string = "unknown";

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

  sayHello(): void {
    console.log(`Hello, my name is ${this.name}. I'm ${this.age || "unknown"} years old. My gender is ${this.gender}.`);
  }
}

let person1 = new Person("Alice");
person1.sayHello(); // 输出:Hello, my name is Alice. I'm unknown years old. My gender is unknown.

let person2 = new Person("Bob");
person2.age = 25;
person2.gender = "male";
person2.sayHello(); // 输出:Hello, my name is Bob. I'm 25 years old. My gender is male.

在这个例子中,我们定义了一个 Person 类,它包含了一个必需属性 name、一个可选属性 age 和一个带有默认值的属性 gender。在构造函数中,我们只需传递必需属性 name,可选属性 age 和默认值属性 gender 可以选择性地进行赋值。在 sayHello 方法中,我们输出相应的属性值,并处理可选属性的不存在情况。

24. 类的只读属性

TypeScript 提供了只读属性(Readonly Properties)的功能,用于限制类的某些属性只能在初始化时赋值,之后无法修改。

class Person {
  readonly name: string;

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

let person = new Person("Alice");
console.log(person.name); // 输出:Alice

person.name = "Bob"; // 错误!只读属性无法修改

在这个例子中,我们定义了一个 Person 类,它包含了一个只读属性 name。只读属性只能在构造函数中进行赋值,之后无法修改。尝试对只读属性进行赋值会导致编译错误。

25. 类的索引签名

类的索引签名(Index Signatures)用于定义类的索引类型,允许类像数组或对象一样使用索引访问属性。

class Dictionary<T> {
  private items: { [key: string]: T } = {};

  addItem(key: string, value: T): void {
    this.items[key] = value;
  }

  getItem(key: string): T {
    return this.items[key];
  }

  getAllItems(): { [key: string]: T } {
    return this.items;
  }
}

let dictionary = new Dictionary<number>();
dictionary.addItem("one", 1);
dictionary.addItem("two", 2);

console.log(dictionary.getItem("one")); // 输出:1

let allItems = dictionary.getAllItems();
console.log(allItems); // 输出:{ one: 1, two: 2 }

在这个例子中,我们定义了一个 Dictionary 类,它包含了一个私有属性 items,它的类型为索引签名 { [key: string]: T },表示可以根据字符串类型的键访问对应的值。通过 addItem 方法添加键值对,通过 getItem 方法根据键获取对应的值,通过 getAllItems 方法获取所有键值对。

26. 类的继承与多态

类的继承是指一个类可以继承另一个类的属性和方法,从而形成类的层次结构。子类可以继承父类的特性,并可以根据需要进行扩展和重写,实现多态的特性。

class Animal {
  protected name: string;

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

  makeSound(): void {
    console.log("Animal is making a sound.");
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name);
  }

  makeSound(): void {
    console.log("Dog is barking.");
  }
}

class Cat extends Animal {
  constructor(name: string) {
    super(name);
  }

  makeSound(): void {
    console.log("Cat is meowing.");
  }
}

let animal: Animal = new Animal("Animal");
let dog: Animal = new Dog("Dog");
let cat: Animal = new Cat("Cat");

animal.makeSound(); // 输出:Animal is making a sound.
dog.makeSound(); // 输出:Dog is barking.
cat.makeSound(); // 输出:Cat is meowing.

在这个例子中,我们定义了一个 Animal 类作为基类,它包含了一个受保护的属性 name 和一个 makeSound 方法。然后,我们创建了 Dog 类和 Cat 类作为子类,它们继承了 Animal 类,并重写了 makeSound 方法。通过创建基类和子类的实例,并调用 makeSound 方法,我们可以看到不同对象的多态行为。

28. 类型断言与类的使用

类型断言是一种方式,用于告诉编译器某个值的具体类型,以便在代码中使用该类型的方法和属性。

class Animal {
  name: string;

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

  eat(): void {
    console.log(`${this.name} is eating.`);
  }
}

let animal: Animal = new Animal("Animal");
(animal as Animal).eat(); // 输出:Animal is eating.

在这个例子中,我们定义了一个 Animal 类,它包含了一个属性 name 和一个方法 eat。通过创建 Animal 类的实例,并进行类型断言,我们可以在代码中使用 eat 方法。

28. 类型推断与类的使用

TypeScript 可以根据上下文自动推断变量的类型。在使用类时,我们可以利用类型推断来简化代码。

class Person {
  constructor(public name: string) {}

  sayHello(): void {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

let person = new Person("Alice");
person.sayHello(); // 输出:Hello, my name is Alice.

在这个例子中,我们定义了一个 Person 类,它包含了一个属性 name 和一个方法 sayHello。在构造函数中,我们使用参数属性来简化代码,将参数 name 声明为类的属性,并进行初始化。通过创建 Person 类的实例,并调用 sayHello 方法,我们可以输出相应的结果。

这些是 TypeScript 类的相关知识和高阶使用方法的一些内容,希望对大家有所帮助。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值