ts 學習筆記 - 類

[TOC]typescript

類的概念

  • 類 (class) : 定義了意見事物的抽象特色,包含它的屬性和方法
  • 對象 (Object) :類的實例,經過 new 生成
  • 面對對象 (OOP) 的三大特性: 封裝、繼承、多態
  • 封裝 (Encapsulation):將對數據的操做細節隱藏起來,值暴露對外的接口。外界調用端不須要(也不可能)知道細節,就能經過對外提供的接口來訪問該對象,同時也保證了外界沒法任意更改對象內部的數據
  • 繼承 (Inheritance): 子類繼承父類, 子類除了擁有父類的全部特性外, 還有一些更具體的特性
  • 多態 (Polymorphism):由繼承二產生了相關的不一樣的類,對同一個方法能夠有不一樣的響應。好比 CatDog 都是繼承自 Animal, 可是分別實現了本身的 eat 方法。此時針對某一個實例,咱們無需瞭解它是 Cat 仍是 Dog, 就能夠直接調用 eat 方法, 程序會自動判斷出來應該如何執行 eat
  • 存取器 (getter & setter):用來改變屬性的讀取和賦值行爲
  • 修飾符 (Modifiers):修飾符是一些關鍵字,用於限定成員或類型的性質。好比 public 表示公有屬性或方法
  • 抽象類(Abstract Class):抽象類是供其餘類繼承的基類,抽象類不容許被實例化。抽象類中抽象方法避稅在子類中被實現
  • 接口(Interfaces):不一樣類之間公有的屬性或方法,能夠抽象成一個接口。接口能夠被類實現(implements)。一個類只能繼承自另外一個類, 可是能夠實現多個接口

類的用法

屬性和方法

使用 class 定義類, 使用 constructor 定義構造函數。函數

經過 new 生成新實例的時候, 會自動調用構造函數。this

class Animal {
  constructor(name) {
    this.name = name
  }
  sayHi() {
    return `My name is ${this.name}`
  }
}

let a = new Animal('jack')
console.log( a.sayHi() ) // My name is Jack

類的繼承

使用 extends 關鍵字實現繼承, 子類中使用 super 關鍵字來調用父類的構造函數和方法。spa

class Cat extends Animal {
  constructor(name) {
    super(name) // 調用父類的 constructor(name)
    console.log(this.name)
  }
  sayHi() {
    return 'Meow, ' + super.sayHi() // 調用父類的 sayHi()
  }
}
let c = new Cat('Tom') // Tom
console.log(c.sayHi()) // Meow, My name is Tom

存取器

使用 getter 和 setter 能夠改變屬性的賦值和讀取行爲:3d

class Animal {
  constructor(name) {
    this.name = name
  }
  
  get name() {
    return 'Jack'
  }
  set name(value) {
    console.log('setter: ' + value)
  }
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

靜態屬性

到目前爲止,咱們只討論了類的實例成員,哪些僅當類被梳理化的時候纔會被初始化的屬性。code

使用 static 修飾符修飾的方法稱爲靜態方法,它們不須要實例化,而是直接經過類來調用。對象

下例中,咱們實現一個網格類, 經過 calculateDistanceFromOrigin 方法來當前點計算到原點的距離。繼承

class Grid {
  static origin = { x: 0, y: 0 }
  
  scale: number
  
  constructor(scale: number) {
    this.scale = scale
  }
  
  calculateDistanceFromOrigin(point: { x: number, y: number }) {
    let xDist = point.x - Grid.origin.x
    let yDist = point.y - Grid.origin.y
		return Math.sqrt(xDist * xDist + yDist * yDist) * this.scale
  }
}
let grid1 = new Grid(1.0) // scale => 1
let grid5 = new Grid(5.0) // scale => 5

grid1.calculateDistanceFromOrigin({x: 3, y: 4}) 
grid5.calculateDistanceFromOrigin({x: 3, y: 4})

Typescript 中的用法

public, private, protected接口

Typescript 中可使用三種訪問修飾符(Access Modifiers),分別是 public, private, protectedip

  • public 修飾的屬性或方法都是公有的, 在任何地方均可以訪問到。默認的全部屬性和方法都是 public 的 。
  • private 修飾的屬性或方法都是私有的,不能再申明它的類的外部去訪問。
  • protected 修飾的屬性或方法是受保護的,它和 private 相似, 區別是它的子類中也是容許被訪問。

🌰

class Person {
  public name
  public constructor(name) {
    this.name = name
  }
}

let p = new Person('Jack')
console.log(p.name) // Jack
p.name = 'Tom'
console.log(p.name) // Tom

上面的例子中 name 被設置了 public , 因此直接訪問 name 是被容許的。

不少時候,咱們但願有的屬性是沒法在外部直接存取。 這時候就須要用到 private

class Person {
  private name
  constructor(name) {
    this.name = name
  }
}

let p = new Person('Jack')
console.log(p.name) // Jack
p.name = 'Tom' // err Property 'name' is private...

使用 private 修飾的屬性或方法,在子類中也是不容許訪問的:

class Person {
  private name
  constructor(name) {
    this.name = name
  }
}

class Male extends Person {
  constructor(name) {
    super(name)
    console.log(this.name)
  }
}

// err

須要注意的是,TypeScript 編譯以後的代碼中,並無限制 private屬性在外部的可訪問性。

而若是是用 protected 修飾, 怎容許在子類中訪問:

class Animal {
    protected name;
    public constructor(name) {
        this.name = name;
    }
}

class Cat extends Animal {
    constructor(name) {
        super(name);
        console.log(this.name);
    }
}

抽象類

abstract 用於定義抽象類和其中的抽象方法。

什麼是抽象類?

首先抽象類不容許實例化,

abstract class Persong {
  public name
 	public constructor(name) {
    this.name = name
  }
  public abstract sayHi()
}

let p = new Person('Jack') // err

上面的例子中,咱們定義了一個抽象類 Animal,而且定義了一個抽象方法 sayHi。在實例化抽象類的時候報錯了。

其次,抽象類中的抽象方法必須被子類實現:

abstract class Person {
 	constructor(name) {
    this.name = name
  }
  public abstract sayHi()
}

class Male extends Persong {
    public eat() {
        console.log(`${this.name} is eating.`)
    }
}

let Jack = new Male('jack') // err Male 中 並無實現 Perosn 中的 sayHi 方法

上例中因爲 Male 中沒有實現 父類 Person 中的 sayHi 方法, 因此 編譯報錯

abstract class Person {
 	constructor(name) {
    this.name = name
  }
  public abstract sayHi()
}

class Male extends Persong {
    public sayHi() {
        console.log(`Meow, My name is ${this.name}`)
    }
}
let Jack = new Male('jack')  // success

類的類型

類的類型 與接口類似

class Person {
	name: sting
  constructor(name: sting) {
    this.name = name
  }
  sayHi(): string {
    return `My name is ${this.name}.`
  }
}

let p: Person = new Person('Jack')
console.log(a.sayHi()) // My name is Jack

類與接口

類實現接口

實現(implements)是面對對象中一個很重要的概念。通常來說,一個類只能繼承自另一個類,有的時候不一樣類之間存在着一些能夠共用的特性,這時能夠把這些特性提取成接口 (interfaces),用 implements 關鍵字來實現。

舉例來講,門是一個類, 防盜門是門的子類。若是防盜門有報警功能, 這時咱們能夠簡單的給防盜門添加一個報警的方法。這時候若是有另外一類, 車。 車子也須要有報警功能。此時就能夠考慮吧報警器這個功能提取出來, 做爲一個接口,防盜門和車都去實現它。

interface Alarm {
  alert()
}

class Door {
  
}

class SecurityDoor extends Door implements Alarm {
  alert() {
    console.log('SecurityDoor alert')
  }
}

class Car implements Alarm {
  alert() {
    console.log('Car alert')
  }
}

一個類能夠實現多個接口:

interface Alarm {
  alert()
}

interface Light {
    lightOn()
    lightOff()
}

class Car implements Alarm, Light {
    alert() {
        console.log('Car alert')
    }
    lightOn() {
        console.log('Car light on')
    }
    lightOff() {
        console.log('Car light off')
    }
}

上面的例子中, Car 實現了 Alarm, Light 接口, 既能報警, 也能開關車燈。

接口繼承接口

接口與接口之間能夠是繼承關係:

經過使用 extends 來繼承

interface Alarm {
  alert()
}

interface Light extends Alarm {
    lightOn()
    lightOff()
}

class Car implements Light {
    alert() {
        console.log('Car alert')
    }
    lightOn() {
        console.log('Car light on')
    }
    lightOff() {
        console.log('Car light off')
    }
}

接口繼承類

接口也能夠繼承類:

class Point {
  x: number
  y: number
}

interface Point3D extends Poing {
  z: number
}

let point3d: Point3D = {x: 1, y: 2, z: 3}

##混合類型

咱們知道,可使用接口的方式來定義一個函數須要符合的形狀:

interface SearchFunc {
    (source: string, subString: string): boolean
}

let mySearch: SearchFunc
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1
}

有時候,一個函數還能夠有本身的屬性和方法:

interface Counter {
    (start: number): string
    interval: number
    reset(): void
}
    
function getCounter(): Counter {
    let counter = <Counter>function (start: number) { }
    counter.interval = 123
    counter.reset = function () { }
    return counter
}

let c = getCounter()
c(10)
c.reset()
c.interval = 5.0
相關文章
相關標籤/搜索