1. 程式人生 > 其它 >可不要忽視了TypeScript中函式和類的重要性

可不要忽視了TypeScript中函式和類的重要性

上一篇文章總結了 TypeScript的型別註解,這一篇來聊聊同樣重要的函式和類

函式

以下聲明瞭一個函式型別,通過type來定義類型別名,void 表示沒有返回值

type fnType = () => void;
作為引數

函式可以作為引數,傳遞到另一個函式中

type fnType = () => void;
function foo(fn: fnType) {
 fn();
}
function bar() {
 console.log("bar");
}
foo(bar);      // bar

與js程式碼不同的點在於傳入的引數需要定義型別註解

定義函式

定義函式的時候,需要給入參指定型別註解,返回值如果可以自行推匯出來,也可以不寫,如 add 和 minus 函式,但是作為引數時,是必須要寫的,如 calc 函式中的入參 fn

function add(num1: number, num2: number): number {
 return num1 + num2;
}
function minus(num1: number, num2: number): number {
 return num1 - num2;
}
function calc(
 num1: number,
 num2: number,
 fn: (num1: number, num2: number) => void
) {
 console.log(fn(num1, num2));
}

calc(30, 20, add);      // 50
calc(30, 20, minus);      // 10
函式引數的型別

ts中函式會規定引數的型別和個數,當個數不確定時,可以使用可選型別、剩餘引數、預設值

可選型別
可選型別相當於該定義的型別和undefined的聯合型別,所以引數有三種選擇、傳入該型別、不傳或者undefined

function foo(x: number, y?: number) {
 console.log(x, y);
}
foo(1, 2);      // 1 2
foo(3);      // 3 undefined
foo(4, undefined);      // 4 undefined

引數預設值
引數設定了預設值就使之稱為了可選型別,不過有預設值的引數最好放在必傳引數後面

function baz(x: number = 20, y: number) {
 console.log(x, y);
}
baz(10, 20);      // 10 20
baz(undefined, 20);      // 20 20

剩餘引數
剩餘引數要放在必傳引數之後

function sum(num: number, ...args: number[]) {
 console.log(num, args);
}
sum(10);      // 10 []
sum(10, 20);      // 10 [20]
sum(10, 20, 30);      // 10 [20, 30]
this的預設推導

在物件的方法中定義的this,ts是可以自動推導的,但是獨立函式中的this,是推導不出來的。

必須要在獨立函式中定義this的型別

type ThisType = { name: string };
const eating = function (this: ThisType) {
 console.log(this.name + " eating~");
};

const person = {
 name: "kiki",
 eating,
};
person.eating()
函式過載

函式過載指的是函式名相同,引數個數或者型別不同,所定義的多個處理方法。

比如需要對字串拼接或者數字求和操作,雖然我們知道 + 號可以用在字串和數字上,但是在型別檢測嚴格的ts程式碼中,這樣寫編譯是不通過的,需要使用【型別縮小】,縮小型別的判斷,再進行處理。

通過聯合型別,引數組合的可能性越多,需要越多的if語句來進行判斷,並且函式的返回值型別也是未知的,在這種情況下可以使用【函式過載】

在ts中,定義函式名和引數型別的過載函式,再通過實現函式來定義具體實現。 會根據資料型別在過載函式中呼叫,再執行實現函式的程式碼。

function add(x: number, y: number): number;
function add(x: string, y: string): string;

function add(x: any, y: any) {
 return x + y;
}
console.log(add(1, 2));
console.log(add("a", "b"));

如果傳遞的引數與過載函式中定義引數不同,是無法通過編譯的。

初始化

類中定義的變數需要初始化,可以在定義類的時候就賦值或者通過constructor來操作

class Person {
 name: string;
 age: number;
 constructor(name: string, age: number) {
  this.name = name;
  this.age = age;
 }
}
const person = new Person("alice", 20);
繼承

ts和js中一致,都是通過 extends 實現繼承,使用父級引數和方法時使用 super 關鍵字。

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

class Student extends Person {
 sno: number;
 constructor(name: string, age: number, sno: number) {
  super(name, age);
  this.sno = sno;
 }
}
const student = new Student("alice", 20, 1);
多型

使用多型可以寫出更加具備通用性的程式碼,如果想要實現多型首先得有繼承,並且父類引用指向子類物件。

class Animal {
 action() {
  console.log("animal action");
 }
}
class Dog extends Animal {
 action() {
  console.log("dog running");
 }
}
class Fish extends Animal {
 action() {
  console.log("fish swimming");
 }
}
function doAction(animals: Animal[]) {
 animals.forEach((animal) => {
  animal.action();
 });
}
doAction([new Dog()]);
doAction([new Dog(), new Fish()]);
doAction([new Dog(), new Fish(), new Animal()]);

這裡相當於 const animal1: Animal = new Dog() ,看起來是 Animal 物件,其實是 Dog 物件,這裡的父類引用指向的是子類物件,所以最後執行的是 Dog 物件的方法

成員修飾符

成員修飾符有以下三種

  • public 表示共有的,任何地方都可見,當沒有定義成員修飾符時,預設為public
  • private 私有的,只有類中能夠訪問到
  • protected 被保護的,表示類自身和子類可以訪問到

public

class Person {
 public username: string = "alice";
 getName() {
  return this.username;
 }
}
const person = new Person();
console.log(person.username);

private
通過private修飾的變數,在例項物件上也是不可訪問的。

protected
通過protected修飾的變數,在例項物件上也是不可訪問的。

readonly

readonly表示該屬性只讀,初始化了之後就不能以任何方式修改,無論是在類內部,還是修改例項物件,但當它是一個物件時,只要不改變它的記憶體地址,修改物件的屬性是可以的。

訪問器

訪問器給私有屬性提供get/set方法,讓我們在類外部獲取/修改私有屬性

class Person {
 private _name: string;
 constructor(newName: string) {
  this._name = newName;
 }
 get name() {
  return this._name;
 }
 set name(newName) {
  if (newName) this._name = newName;
 }
}

const person = new Person("alice");
console.log(person.name);
person.name = "kiki";
console.log(person.name);
person.name = "";
console.log(person.name);

通過get/set屬性來修改私有屬性可以做到攔截/判斷的作用

靜態成員

靜態成員通過 static 關鍵字來定義,通過 static 定義的屬性,是定義在類自身的,只能通過自己訪問,在類內部和例項物件都是無法訪問到的。

抽象類

在定義很多通用的呼叫介面時,我們通常會讓呼叫者傳入父類,通過多型來實現更加靈活的呼叫方式。

但是,父類本身可能並不需要對某些方法進行具體的實現,所以父類中定義的方法, 我們可以定義為抽象方法。

abstract class Shape {
 abstract getArea(): void;
}

class Circle extends Shape {
 private radius: number;
 constructor(radius: number) {
  super();
  this.radius = radius;
 }
 getArea() {
  return this.radius * this.radius * 3.14;
 }
}

class Rectangle extends Shape {
 private width: number;
 private height: number;
 constructor(width: number, height: number) {
  super();
  this.width = width;
  this.height = height;
 }
 getArea() {
  return this.width * this.height;
 }
}

function calcArea(shape: Shape) {
 return shape.getArea();
}

console.log(calcArea(new Circle(3)));
console.log(calcArea(new Rectangle(2, 6)));

抽象方法和方法通過 abstract 來修飾,並且抽象類定義時有兩條規則:

  • 抽象方法必須要在子類實現
  • 抽象類是不能被例項化的
類的型別

類本身也是可以作為一種資料型別的,可以用作型別註解。

class Person {
 name: string = "alice";
 eating() {}
}
const person: Person = {
 name: "kiki",
 eating() {
  console.log("i am eating");
 },
};
function printPerson(person: Person) {
 console.log(person.name);
}

printPerson(new Person());
printPerson(person);
printPerson({ name: "macsu", eating() {} });

只要符合類的格式,就可以使用類的型別

函式和類在JS和TS中都是至關重要的,可以幫助開發者更好規範開發時的程式碼,減少線上故障~

以上就是關於TypeScript函式和類的內容,關於js和ts,還有很多需要開發者掌握的地方,可以看看我寫的其他博文,持續更新中~