本文目錄:
- 1.創(chuàng)建對象實例
- 2.繼承
- 3.靜態(tài)方法和屬性
- 4.訪問修飾符
- 5.readonly
- 6.抽象類
1.創(chuàng)建對象實例
在js中峦筒,生成實例對象的傳統(tǒng)方法是通過構造函數(shù)
我們首先通過傳統(tǒng)的構造函數(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)建對象實例');
console.log(m1.msg);
console.log(m1.greeter());
接下來我們再通過類class的方式 生成一個對象實例
我們在ES6的時候悬嗓,實例屬性都是定義在constructor()方法里面垄潮, 在ES7里 我們可以直接將這個屬性定義在類的最頂層,其它都不變放刨,去掉this每瞒,msg和flag就是我們定義在最頂層的實例屬性
constructor(構造函數(shù))方法是類的默認方法
一個類必須有constructor方法,如果沒有顯示定義届氢,一個空的constructor方法會被默認添加
class Greeter {
msg: string;
flag: boolean = false;
// 關于構造函數(shù):
constructor(message: string) {
this.msg = message;
}
greeter() {
console.log('這個是在constructor構造函數(shù)外部定義實例屬性:', this.flag);
return 'hello: ' + this.msg;
}
}
let g2 = new Greeter('通過類創(chuàng)建的對象實例');
console.log(g2.msg);
console.log(g2.greeter());
接下來我們來分析一些,ES6新增的class語法糖亥至,和構造函數(shù)的一些關系
類class的類型 本質(zhì)上是一個函數(shù); 類本身就指向自己的構造函數(shù)
console.log(typeof Greeter);
console.log(Greeter === Greeter.prototype.constructor);
console.log(g2.greeter === Greeter.prototype.greeter);
通過上面在這個代碼我們也可以發(fā)現(xiàn)悼沈,new類的時候就相當于new構造函數(shù)
調(diào)用類上面的方法就是調(diào)用原型上的方法
在類的實例上面調(diào)用方法,其實就是調(diào)用原型上的方法
2.繼承
- 使用繼承來擴展現(xiàn)有的類姐扮,是面向?qū)ο蟮娜筇匦灾?封裝絮供,繼承,多態(tài))
- 基類茶敏,父類壤靶,超類是指被繼承的類,派生類惊搏,子類是指繼承于基類的類
- ts中類繼承類似于傳統(tǒng)面向?qū)ο缶幊陶Z言中的繼承體系 贮乳,使用extends關鍵字繼承,類中this表示此當前對象本身恬惯,super表父類對象向拆。子類構造函數(shù)中第一行代碼調(diào)用父類構造函數(shù)完成初始化,然后再進行子類的進一步初始化酪耳。子類中可以訪問父類(public浓恳、protected)的成員屬性、方法
- 派生類包含了constructor; ts 規(guī)定只要派生類里面自定義了一個constructor函數(shù)就必須在使用this前碗暗,調(diào)用一下super方法
- ES5 的繼承颈将,實質(zhì)是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this));ES6 的繼承機制完全不同言疗,實質(zhì)是先將父類實例對象的屬性和方法晴圾,加到this上面(所以必須先調(diào)用super方法),然后再用子類的構造函數(shù)修改this
- 因為子類自己的this對象噪奄,必須先通過父類的構造函數(shù)完成塑造死姚,得到與父類同樣的實例屬性和方法,然后再對其進行加工勤篮,加上子類自己的實例屬性和方法知允。如果不調(diào)用super方法,子類就得不到this對象
子類方法名和父類相同表示重寫父類方法
業(yè)務需求:我們現(xiàn)在有兩個類叙谨,一個動物類温鸽,一個狗類, 狗也是動物手负,所以會繼承動物類的一些屬性和方法
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();
上面這個例子中 動物類是基類涤垫,也可以叫父類; 狗是子類也可以叫派生類竟终, 繼承自動物類蝠猬,可以使用父類的任何方法和屬性
我們將上面的代碼稍微做一下修改
class Dog extends Animal {
dogName2: string;
constructor(name: string) {
// 派生類包含了一個構造函數(shù),就必須首先調(diào)用super()方法统捶,會調(diào)用基類的構造函數(shù)榆芦,然后構造子類自己的this
super(name);
this.dogName2 = name;
}
// 父類也有一個move方法柄粹,我們在子類例自定義move方法,就會重寫從Animal繼承來的move方法匆绣,從而使move方法根據(jù)不同的類而實現(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);
這個dog1即使被聲明為 Animal類型驻右,也不會調(diào)用父類的move方法,因為它的值就是Dog實例
3.靜態(tài)方法和屬性
- ES6中提供了 靜態(tài)方法崎淳, ES7中提供了靜態(tài)屬性堪夭; TS兩者都有
- 我們可以認為類具有 實例部分與 靜態(tài)部分這兩個部分。定義靜態(tài)屬性和方法拣凹,只需要在對應的屬性和方法前面加上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)用的屬性和方法 叫做對象實例的屬性和方法
通過類名Animal來調(diào)用的 叫靜態(tài)屬性和方法
4.訪問修飾符
- ts類中修飾符分為3種森爽; public : 公有(所有)默認;
protected:保護 (父類+子類)嚣镜;private: 私有(本類)
class Animal {
public name: string;
//修飾符還可以使用在構造函數(shù)參數(shù)中爬迟,等同于類中定義該屬性,使代碼更簡潔
//下面的age屬性就相當于定義在頂部的 一個實例屬性菊匿,借助修飾符也可以定義
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 被設置為了 public,所以直接訪問實例的 name 屬性是允許的
在ts中捧请,所有的類型默認都是public
- private: 當成員被標記成 private時凡涩,它就不能在聲明它的類的外部訪問
class Animal {
// 這個name屬性就只能在這個類里面訪問,類外部訪問就會報錯
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Dog extends Animal {
constructor(name) {
// 派生類的構造函數(shù)必須包含super函數(shù)的調(diào)用
// 因為父類的構造函數(shù)需要一個參數(shù)疹蛉,所以這里我們需要將name參數(shù)傳遞進去
super(name);
// console.log(this.name); //屬性“name”為私有屬性活箕,只能在類“Animal”中訪問。所以在派生類里面訪問也是不允許的
}
}
let a1 = new Animal('Lucy');
console.log(a1.name);
- protected: 屬性和方法 如果是用 protected 修飾可款,則允許在派生類中訪問育韩, private是不允許的
class Animal {
// 這個name屬性就只能在這個類里面訪問,類外部訪問就會報錯
protected name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Dog extends Animal {
constructor(name) {
super(name);
// 這個基類的name屬性是 protected受保護的闺鲸,所以可以在派生類里面訪問
console.log(this.name);
}
}
let a1 = new Animal('Lucy');
- 構造函數(shù)被private修飾, 該類不允許被繼承或者實例化筋讨;只允許被繼承
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
- 只讀屬性關鍵字,只允許出現(xiàn)在屬性聲明或索引簽名中
- 可以使用 readonly關鍵字將屬性設置為只讀的摸恍。 只讀屬性必須在聲明時或構造函數(shù)里被初始化
class Animal {
readonly name: string;
// 聲明是初始化
readonly myName: string = '只讀屬性';
// 注意如果 readonly 和其他訪問修飾符同時存在的話悉罕,需要寫在其后面
constructor(name: string, public readonly firstName: string) {
// 構造函數(shù)里面初始化
this.name = name;
}
}
let cat2 = new Animal('阿黃', '小白');
console.log(cat2.name, cat2.myName, cat2.firstName);
cat2.name = '張三'; // 編譯報錯,說不能給一個只讀屬性分配一個新值
6.抽象類
- 抽象類做為其它派生類的基類使用立镶。 它們一般不會直接被實例化壁袄。 不同于接口,抽象類可以包含成員的實現(xiàn)細節(jié)
- abstract關鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法抽象成員
abstract class Animal {
name: string = '基類默認值';
abstract myName: string;
// 僅僅定義方法的簽名媚媒,不包含方法體
abstract makeSound(): void;
move(): void {
console.log('動物行走');
}
}
下面這行代碼就會報錯嗜逻, 無法創(chuàng)建抽象類的實例
抽象類不能被實例化, 只能作為基類使用缭召,也就是只能給其他類繼承
let aa2 = new Animal()
抽象類中的抽象方法不包含具體實現(xiàn)并且必須在派生類中實現(xiàn)栈顷。 抽象方法的語法與接口方法相似逆日。 兩者都是定義方法簽名但不包含方法體。 然而萄凤,抽象方法必須包含 abstract關鍵字并且可以包含訪問修飾符
class Dog extends Animal {
myName: string = '抽象成員';
// 編譯報錯:非抽象類“Dog”不會實現(xiàn)繼承自“Animal”類的抽象成員“makeSound”
// 也就是說我們要將基類的抽象方法在派生類這里再實現(xiàn)一次
makeSound() {
console.log(`基類的抽象方法必須在派生類中實現(xiàn)--${this.name}--${this.myName}`);
}
}
let aa3 = new Dog();
console.log(aa3.makeSound());