TS(四)類

本文目錄:

  • 1.創(chuàng)建對象實(shí)例
  • 2.繼承
  • 3.靜態(tài)方法和屬性
  • 4.訪問修飾符
  • 5.readonly
  • 6.抽象類

1.創(chuàng)建對象實(shí)例

在js中,生成實(shí)例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù)
我們首先通過傳統(tǒng)的構(gòu)造函數(shù)和原型對象的方法來看一下對象實(shí)例的創(chuàng)建

function Greeter(message) {
  this.msg = message;
}
Greeter.prototype.greeter = function() {
  return 'hello: ' + this.msg;
};
let m1 = new Greeter('傳統(tǒng)方式創(chuàng)建對象實(shí)例');
console.log(m1.msg);
console.log(m1.greeter());

接下來我們再通過類class的方式 生成一個(gè)對象實(shí)例
我們在ES6的時(shí)候,實(shí)例屬性都是定義在constructor()方法里面, 在ES7里 我們可以直接將這個(gè)屬性定義在類的最頂層,其它都不變,去掉this,msg和flag就是我們定義在最頂層的實(shí)例屬性
constructor(構(gòu)造函數(shù))方法是類的默認(rèn)方法
一個(gè)類必須有constructor方法,如果沒有顯示定義,一個(gè)空的constructor方法會被默認(rèn)添加

class Greeter {
  msg: string;
  flag: boolean = false;
  // 關(guān)于構(gòu)造函數(shù):
  constructor(message: string) {
    this.msg = message;
  }
  greeter() {
    console.log('這個(gè)是在constructor構(gòu)造函數(shù)外部定義實(shí)例屬性:', this.flag);
    return 'hello: ' + this.msg;
  }
}
let g2 = new Greeter('通過類創(chuàng)建的對象實(shí)例');
console.log(g2.msg);
console.log(g2.greeter());

接下來我們來分析一些,ES6新增的class語法糖,和構(gòu)造函數(shù)的一些關(guān)系
類class的類型 本質(zhì)上是一個(gè)函數(shù); 類本身就指向自己的構(gòu)造函數(shù)

console.log(typeof Greeter);
console.log(Greeter === Greeter.prototype.constructor);
console.log(g2.greeter === Greeter.prototype.greeter);

通過上面在這個(gè)代碼我們也可以發(fā)現(xiàn),new類的時(shí)候就相當(dāng)于new構(gòu)造函數(shù)
調(diào)用類上面的方法就是調(diào)用原型上的方法
在類的實(shí)例上面調(diào)用方法,其實(shí)就是調(diào)用原型上的方法

2.繼承

  1. 使用繼承來擴(kuò)展現(xiàn)有的類,是面向?qū)ο蟮娜筇匦灾?封裝,繼承,多態(tài))
  2. 基類,父類,超類是指被繼承的類,派生類,子類是指繼承于基類的類
  3. ts中類繼承類似于傳統(tǒng)面向?qū)ο缶幊陶Z言中的繼承體系 ,使用extends關(guān)鍵字繼承,類中this表示此當(dāng)前對象本身,super表父類對象。子類構(gòu)造函數(shù)中第一行代碼調(diào)用父類構(gòu)造函數(shù)完成初始化,然后再進(jìn)行子類的進(jìn)一步初始化。子類中可以訪問父類(public、protected)的成員屬性、方法
  4. 派生類包含了constructor; ts 規(guī)定只要派生類里面自定義了一個(gè)constructor函數(shù)就必須在使用this前,調(diào)用一下super方法
  • ES5 的繼承,實(shí)質(zhì)是先創(chuàng)造子類的實(shí)例對象this,然后再將父類的方法添加到this上面(Parent.apply(this));ES6 的繼承機(jī)制完全不同,實(shí)質(zhì)是先將父類實(shí)例對象的屬性和方法,加到this上面(所以必須先調(diào)用super方法),然后再用子類的構(gòu)造函數(shù)修改this
  • 因?yàn)樽宇愖约旱膖his對象,必須先通過父類的構(gòu)造函數(shù)完成塑造,得到與父類同樣的實(shí)例屬性和方法,然后再對其進(jìn)行加工,加上子類自己的實(shí)例屬性和方法。如果不調(diào)用super方法,子類就得不到this對象

子類方法名和父類相同表示重寫父類方法
業(yè)務(wù)需求:我們現(xiàn)在有兩個(gè)類,一個(gè)動物類,一個(gè)狗類, 狗也是動物,所以會繼承動物類的一些屬性和方法

class Animal {
  name: string;
  constructor(param: string) {
    this.name = param;
  }
  move(distance: number = 0) {
    console.log(`${this.name} 移動了 ${distance}m.`);
  }
}
class Dog extends Animal {
   bark() {
      console.log('狗叫!');
   }
}  
const dog = new Dog('阿黃');
console.log(dog.name);
dog.bark();
dog.move(10);
dog.bark();

上面這個(gè)例子中 動物類是基類,也可以叫父類; 狗是子類也可以叫派生類, 繼承自動物類,可以使用父類的任何方法和屬性
我們將上面的代碼稍微做一下修改

class Dog extends Animal {
  dogName2: string;
  constructor(name: string) {
   // 派生類包含了一個(gè)構(gòu)造函數(shù),就必須首先調(diào)用super()方法,會調(diào)用基類的構(gòu)造函數(shù),然后構(gòu)造子類自己的this
    super(name);
    this.dogName2 = name;
  }
  // 父類也有一個(gè)move方法,我們在子類例自定義move方法,就會重寫從Animal繼承來的move方法,從而使move方法根據(jù)不同的類而實(shí)現(xiàn)不同的功能
  move(distanceInMeters: number = 5) {
    console.log('重寫了基類的move方法');
    super.move(distanceInMeters);
  }
  bark() {
    console.log('狗叫!');
  }
}
let animal1: Animal = new Animal('赤兔');
let dog1: Dog = new Dog('阿黃');
animal1.move();
dog1.move(10);

這個(gè)dog1即使被聲明為 Animal類型,也不會調(diào)用父類的move方法,因?yàn)樗闹稻褪荄og實(shí)例

3.靜態(tài)方法和屬性

  1. ES6中提供了 靜態(tài)方法, ES7中提供了靜態(tài)屬性; TS兩者都有
  2. 我們可以認(rèn)為類具有 實(shí)例部分與 靜態(tài)部分這兩個(gè)部分。定義靜態(tài)屬性和方法,只需要在對應(yīng)的屬性和方法前面加上static即可
class Animal {
  static PI = 3.14159;
  static isAnimal(param) {
    return param instanceof Animal;
  }
}
let cat = new Animal();
console.log(Animal.PI);
console.log(Animal.isAnimal(cat));
cat.isAnimal(cat);
cat.PI;

通過對象cat上來調(diào)用的屬性和方法 叫做對象實(shí)例的屬性和方法
通過類名Animal來調(diào)用的 叫靜態(tài)屬性和方法

4.訪問修飾符

  1. ts類中修飾符分為3種; public : 公有(所有)默認(rèn);
    protected:保護(hù) (父類+子類);private: 私有(本類)
class Animal {
  public name: string;
  //修飾符還可以使用在構(gòu)造函數(shù)參數(shù)中,等同于類中定義該屬性,使代碼更簡潔
  //下面的age屬性就相當(dāng)于定義在頂部的 一個(gè)實(shí)例屬性,借助修飾符也可以定義
   public constructor(theName: string, public age: number = 24) {
     this.name = theName;
   }
   public move() {
      console.log(123);
   }
}
let a1 = new Animal('Lucy');
console.log(a1.name, a1.age);

上面的例子中,name 被設(shè)置為了 public,所以直接訪問實(shí)例的 name 屬性是允許的
在ts中,所有的類型默認(rèn)都是public

  1. private: 當(dāng)成員被標(biāo)記成 private時(shí),它就不能在聲明它的類的外部訪問
class Animal {
  // 這個(gè)name屬性就只能在這個(gè)類里面訪問,類外部訪問就會報(bào)錯(cuò)
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Dog extends Animal {
  constructor(name) {
   // 派生類的構(gòu)造函數(shù)必須包含super函數(shù)的調(diào)用
   // 因?yàn)楦割惖臉?gòu)造函數(shù)需要一個(gè)參數(shù),所以這里我們需要將name參數(shù)傳遞進(jìn)去
    super(name);
    // console.log(this.name); //屬性“name”為私有屬性,只能在類“Animal”中訪問。所以在派生類里面訪問也是不允許的
  }
}
let a1 = new Animal('Lucy');
console.log(a1.name);
  1. protected: 屬性和方法 如果是用 protected 修飾,則允許在派生類中訪問, private是不允許的
class Animal {
  // 這個(gè)name屬性就只能在這個(gè)類里面訪問,類外部訪問就會報(bào)錯(cuò)
  protected name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Dog extends Animal {
  constructor(name) {
    super(name);
    // 這個(gè)基類的name屬性是 protected受保護(hù)的,所以可以在派生類里面訪問
    console.log(this.name);
  }
}
let a1 = new Animal('Lucy');
  1. 構(gòu)造函數(shù)被private修飾, 該類不允許被繼承或者實(shí)例化;只允許被繼承
class Animal {
  public name;
  private constructor(name) {
    this.name = name;
  }
  // protected constructor(name) {
  //   this.name = name;
  // }
}
class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}
let a = new Animal('Jack');

5.readonly

  • 只讀屬性關(guān)鍵字,只允許出現(xiàn)在屬性聲明或索引簽名中
  • 可以使用 readonly關(guān)鍵字將屬性設(shè)置為只讀的。 只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化
class Animal {
  readonly name: string;
  // 聲明是初始化
  readonly myName: string = '只讀屬性';
  // 注意如果 readonly 和其他訪問修飾符同時(shí)存在的話,需要寫在其后面
  constructor(name: string, public readonly firstName: string) {
    // 構(gòu)造函數(shù)里面初始化
    this.name = name;
  }
}
let cat2 = new Animal('阿黃', '小白');
console.log(cat2.name, cat2.myName, cat2.firstName);
cat2.name = '張三'; // 編譯報(bào)錯(cuò),說不能給一個(gè)只讀屬性分配一個(gè)新值

6.抽象類

  • 抽象類做為其它派生類的基類使用。 它們一般不會直接被實(shí)例化。 不同于接口,抽象類可以包含成員的實(shí)現(xiàn)細(xì)節(jié)
  • abstract關(guān)鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法抽象成員
abstract class Animal {
  name: string = '基類默認(rèn)值';
  abstract myName: string;
  // 僅僅定義方法的簽名,不包含方法體
  abstract makeSound(): void;
  move(): void {
    console.log('動物行走');
  }
}

下面這行代碼就會報(bào)錯(cuò), 無法創(chuàng)建抽象類的實(shí)例
抽象類不能被實(shí)例化, 只能作為基類使用,也就是只能給其他類繼承

let aa2 = new Animal()

抽象類中的抽象方法不包含具體實(shí)現(xiàn)并且必須在派生類中實(shí)現(xiàn)。 抽象方法的語法與接口方法相似。 兩者都是定義方法簽名但不包含方法體。 然而,抽象方法必須包含 abstract關(guān)鍵字并且可以包含訪問修飾符

class Dog extends Animal {
  myName: string = '抽象成員';
  // 編譯報(bào)錯(cuò):非抽象類“Dog”不會實(shí)現(xiàn)繼承自“Animal”類的抽象成員“makeSound”
  // 也就是說我們要將基類的抽象方法在派生類這里再實(shí)現(xiàn)一次
  makeSound() {
    console.log(`基類的抽象方法必須在派生類中實(shí)現(xiàn)--${this.name}--${this.myName}`);
  }
}
let aa3 = new Dog();
console.log(aa3.makeSound());
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容