1. 程式人生 > 其它 >TypeScript的基本使用(2)

TypeScript的基本使用(2)

面向物件

  • 程式是對現實生活的一種抽象,物件是對現實生活的一種模擬;
  • 要對什麼進行操作,就找什麼物件。比如你要操作瀏覽器視窗,就需要使用window物件
  • 事物要在程式中的描述,是以屬性(資料)和方法(功能)
  • 類是物件的模型,可以通過類來建立物件

為了方便後面程式碼的測試,簡單的實現對檔案內容進行監測後自動編譯

  1. 建立tsconfig.json檔案配置如下
{
  "include":["./src/**/*"],
  "compilerOptions":{
    "module": "es2015",
    "target": "es2015",
    "strict": true,
    "outDir": "./dist",
    "noEmitOnError":true
  }
}
  1. 執行tsc -w命令

  • 類是建立物件的模板
  • 物件包含屬性和方法
class Person{
  // 例項屬性
  name:string = '唐僧';
	// 靜態屬性
	static country:string = 'China';
	// 只讀屬性
	readonly sex:string = 'male';

	static readonly color:string = 'yellow':
  
  // 靜態方法
  static sayHi(){
    console.log('say hi')
  }
	
	// 例項方法
  sayName(){
		console.log('okkk')
  }
}

let p1 = new Person()
console.log(p1.name);
// p1.sex = 'update'

console.log(Person.country);
//Person.color = 'pink'
p1.sayHi()

注意:

  • 例項屬性,只能通過物件的例項去訪問
  • 靜態屬性,只能通過類去訪問
  • 只讀屬性,無法修改
  • staic修飾符與readonly一起使用時,必須將static放置在最前面

建構函式

  • 一般通過類建立多個不同的物件,物件中擁有不同的屬性值,所以在類中不能寫固定的值;
  • 什麼時候進行物件的屬性賦值?物件在建立的時候,由外界傳值。建構函式會在建立物件的時候呼叫,即當new 類名()時會呼叫類中的constructor方法
  • 例項方法中this指向是的呼叫的物件(新建立的物件或者呼叫的物件)
  • 為了實現不同的物件中屬性值不同,可以在建構函式中定義形參來接收傳遞的引數值
class Dog{
  name:string;
	
  constructor(name:string){
    this.name = name;
  }
	
  bark(){
		console.log(this.name+' is barking...')
  }
}

let d1 = new Dog('旺財')
d1.bark()

繼承

  • 可以將子類中共有的程式碼都寫到父類中,使用繼承後,子類將會擁有父類所有的方法和屬性。
  • 如果在子類中添加了和父類相同的方法,則子類方法會覆蓋掉父類的方法,這種稱之為方法重寫
class Animal{
  name:string;
  constructor(name:string){
    this.name = name;
  }
  shout(){
		console.log("動物在叫喚")
  }
}

class Cat extends Animal{
  shout(){
    console.log(this.name+'在叫')
  }
}

在開發中我們會經常使用到別人的類(比如:框架的),但是別人的類並不是完全滿足我們的需求,我們不會輕易的改動別人的類,如果改動了別人的類可能會導致在其他地方使用到這個類時報錯的問題。比如:我們需要4個功能,但是A類只有3個功能,我們可以建立新類去繼承這個類,然後在新類中新增新的功能----繼承可以對新的類進行擴充套件。簡而言之,可以新增原來不存在的功能,或者覆蓋父類中不滿足子類要求的方法

super

  • 使用super.方式訪問的話,會呼叫父類例項中的屬性或方法
  • 不管父類是否有重寫構造方法,子類如果重寫構造方法則必須在構造方法的第一行呼叫super,否則報錯
class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
  sayHello() {
    console.log("動物再叫~")
  }
}

class Dog extends Animal {
  age: number;
  constructor(name: string, age: number) {
    super(name);
    this.age = age;
  }
  sayHello() {
    console.log('喵喵叫')
  }
}

let d1 = new Dog('旺財', 12)
d1.sayHello()

抽象類

  • 不希望呼叫者通過這個類建立物件,這個類是基類,可以讓其他類進行繼承;
  • 使用abstract關鍵字定義抽象類,沒有方法體
  • 抽象類的作用:可以讓其他類進行繼承
  • 抽象方法只能出現在抽象類中;繼承了抽象類的類必須實現抽象類中定義的抽象方法
abstract class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  abstract sayHello(): void;
}

class Dog extends Animal {
  sayHello() {
    console.log('旺旺旺')
  }
}

let d1 = new Dog('阿久')
d1.sayHello()

介面

  • 介面用來定義一個類結構,用來定義一個類中應該包含哪些屬性和方法,這些屬性和方法不能有值
  • 介面可以重複宣告,即可以重複使用這個介面名進行定義,在使用時必須對介面中定義的屬性和方法都要進行實現
  • 使用類去實現介面,則必須實現介面中定義的屬性和方法;
  • 介面和抽象類的差異:抽象類可以定義抽象方法,也可以實現具體的方法;而介面中定義的只能是抽象方法
  • 注意:介面和抽象類這些都是在ts中有的,而在js中沒有
interface Person {
  name: string;
  age: number;
}

interface Person {
  weight: number;
}

const obj: Person = {
  name: '如沐春風',
  age: 20,
  weight: 1.88
}

interface myInter{
  name: string;
	sayHello():void;
}
// 類實現介面
class MyClass implements myInter {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHello() {
    console.log('大家好~~');
  }
}

封裝

  • 前面物件的屬性值可以任意的修改,會導致物件中的資料變得非常不安全;使用屬性的封裝可以讓資料變得更安全
  • 可以在屬性前新增屬性修飾符
    • public 修飾的屬性可以在任意位置訪問(修改) 預設值。 【大家共享】
    • private 私有屬性,私有屬性只能在類內部進行訪問(修改) 【我自己珍藏的】
      • 通過在類中新增方法使得私有屬性可以被外部訪問
      • 只能在當前類中訪問,子類不行
    • protected 受包含的屬性,只能在當前類和當前類的子類中訪問(修改),不能在類外訪問 【同一種族共享】
  • 想要在類外獲取private修飾的屬性,則需要在類中定義其他的方法內部訪問這個私有屬性,然後暴露給外部;我們現在可以通過控制getter和setter的方法來對屬性進行控制,可以保證程式更加的健壯
    • getter方法用來讀取屬性
    • setter方法用來設定屬性
    • 它們被稱為屬性的存取器
class Person{
  private _age:number;
  constructor(age:number){
    this._age = age;
  }
  // 獲取
  getAge(){
    return this._age
  }
  // 設定
  setAge(age:number){
    //只有在age合法的情況下才進行操作
    if(age>=0){
      this._age = age
    }
  }
}

const p1 = new Person(20)
//p1._age = -30
p1.setAge(-30)
console.log(p1)

ts為了簡化程式設計師頻繁的編寫getter和setter程式碼,允許使用下面的簡便方式

class Person{
  private _age:number;
  constructor(age:number){
    this._age = age;
  }

  get age(){
    return this._age;
  }

  set age(value:number){
    if(value >=0){
      this._age = value
    }
  }
}
const p2= new Person(20)
console.log(p2)
p2.age = -33        //這裡會呼叫"set age"
console.log(p2)

注意:

  • 不建議外部輕易更改的屬性名一般以"_"開頭
  • 屬性存取器一般在開發中當屬性容易被修改錯或者對屬性值要求比較高時建議使用;

屬性修飾符

class A{
  protected num: number;

  constructor(num: number) {
    this.num = num;
  }
}

class B extends A{

  test(){
    console.log(this.num);
  }

}

const b = new B(123);
// b.num = 33;          報錯,不能是類外修改

可以直接將屬性定義在建構函式中

class C{
  public name:string;
	
  constructor(name:string){
		this.name = name;
  }
}
等價於下面
class C{
  constructor(public name:string){
		this.name = name;
  }
}

泛型

  • 表示某個型別
  • 定義一個函式或類時,有些情況下無法確定其中要使用的具體型別(返回值、引數、屬性的型別不能確定),但是能確定的時其返回值的型別和引數的型別是相同的,這種就需要使用到泛型
  • 泛型的作用:在型別資料型別不明確的時候,通過一個變數來儲存變數的資料型別
// 泛型定義
function fn<T>(a: T): T {
  return a
}

// 呼叫時可以指定泛型或者不指定泛型
let n = fn<number>(10)
let n1 = fn(10)

// 泛型可以同時指定多個
function fn2<M, N>(a: M, b: N): N {
  console.log(a);
  return b
}
fn2(10, 'hello')

泛型與繼承結合

interface Inter {
  length: number;
}
// 指定泛型T必須是Inter的子類或實現類  【可以是包含有Inter介面規範的類】
function fn3<T extends Inter>(a: T): number {
  return a.length
}
f3("hello")         //這裡可以執行,因為字串中含有length屬性 
f3(123)             //報錯
f3({length:10})

泛型與類結合

class myClass<T>{
  name: T;
  constructor(name: T) {
    this.name = name
  }
}

let m = new myClass('hello')