TypeScript與面向對象
- 2021 年 9 月 22 日
- 筆記
- Conclusion
1、引
簡而言之就是程式之中所有的操作都需要通過對象來完成。一切操作都要通過對象,也就是所謂的面向對象,在程式中對象被分成了兩個部分數據和功能,以人為例,人的姓名、性別、年齡、身高等屬於數據,人可以說話、走路、吃飯、睡覺這些都屬於人的功能,數據在對象中被稱為屬性,而功能被稱為方法,所以簡而言之在程式中一切皆對象
2、類(class)
// 使用class關鍵字來定義一個類
/*
* 對象中主要包含了兩個部分:
* 屬性
* 方法
* */
class Person{
/*
* 直接定義的屬性是實例屬性,需要通過對象的實例去訪問:
* const per = new Person();
* per.name
*
* 使用static開頭的屬性是靜態屬性(類屬性),可以直接通過類去訪問
* Person.age
*
* readonly開頭的屬性表示一個只讀的屬性無法修改
* */
// 定義實例屬性
// readonly name: string = '孫悟空';
name = '孫悟空';
// 在屬性前使用static關鍵字可以定義類屬性(靜態屬性)
// static readonly age: number = 18;
age = 18;
// 定義方法
/*
* 如果方法以static開頭則方法就是類方法,可以直接通過類去調用
* */
sayHello(){
console.log('Hello 大家好!');
}
}
const per = new Person();
// console.log(per);
// console.log(per.name, per.age);
// console.log(Person.age);
// console.log(per.name);
// per.name = 'tom';
// console.log(per.name);
// per.sayHello();
// Person.sayHello();
per.sayHello();
readonly 表示一個只讀屬性,不能修改,是在實例上的
static 表示一個靜態屬性,通過類的點語法
3、構造函數和this
class Dog{
name: string;
age: number;
// constructor 被稱為構造函數
// 構造函數會在對象創建時調用
constructor(name: string, age: number) {
// 在實例方法中,this就表示當前當前的實例
// 在構造函數中當前對象就是當前新建的那個對象
// 可以通過this向新建的對象中添加屬性
this.name = name;//第一個name是類的屬性,第二個name是傳進的參數的類型
this.age = age;
}
bark(){
// alert('汪汪汪!');
// 在方法中可以通過this來表示當前調用方法的對象
console.log(this.name);
}
}
const dog = new Dog('小黑', 4);
const dog2 = new Dog('小白', 2);
// console.log(dog);
// console.log(dog2);
dog2.bark();
4、繼承
(function (){
// 定義一個Animal類
class Animal{
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello(){
console.log('動物在叫~');
}
}
/*
* Dog extends Animal
* - 此時,Animal被稱為父類,Dog被稱為子類
* - 使用繼承後,子類將會擁有父類所有的方法和屬性
* - 通過繼承可以將多個類中共有的程式碼寫在一個父類中,
* 這樣只需要寫一次即可讓所有的子類都同時擁有父類中的屬性和方法
* 如果希望在子類中添加一些父類中沒有的屬性或方法直接加就行
* - 如果在子類中添加了和父類相同的方法,則子類方法會覆蓋掉父類的方法,方法可以重寫!!
* 這種子類覆蓋掉父類方法的形式,我們稱為方法重寫
*
* */
// 定義一個表示狗的類
// 使Dog類繼承Animal類
class Dog extends Animal{
run(){
console.log(`${this.name}在跑~~~`);
}
sayHello() {
console.log('汪汪汪汪!');
}
}
// 定義一個表示貓的類
// 使Cat類繼承Animal類
class Cat extends Animal{
sayHello() {
console.log('喵喵喵喵!');
}
}
const dog = new Dog('旺財', 5);
const cat = new Cat('咪咪', 3);
console.log(dog);
dog.sayHello();
dog.run();
console.log(cat);
cat.sayHello();
})();
5、super
子類寫了constructor構造函數必須使用super繼承父類constructor構造函數的屬性
(function () {
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.name = name,偽程式碼
super(name); // 調用父類的構造函數
//如果在子類的構造函數直接super父類構造函數
this.age = age;
}
// 重寫父類方法
sayHello() {
// 在類的方法中 super就表示當前類的父類
// super.sayHello();
console.log('汪汪汪汪!');
}
}
const dog = new Dog('旺財', 3);
dog.sayHello()
})();
6、抽象類
(function () {
/*
* 以abstract開頭的類是抽象類,
* 抽象類和其他類區別不大,只是不能用來創建對象
* 抽象類就是專門用來被繼承的類
*
* 抽象類中可以添加抽象方法
* */
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 定義一個抽象方法
// 抽象方法使用 abstract開頭,沒有方法體
// 抽象方法只能定義在抽象類中,子類必須對抽象方法進行重寫
abstract sayHello():void;
}
class Dog extends Animal{
sayHello() {
console.log('汪汪汪汪!');
}
}
class Cat extends Animal{
sayHello() {
console.log('喵喵喵喵!');
}
}
const dog = new Dog('旺財');
dog.sayHello();
})();
抽象方法只能定義在抽象類中,子類必須對抽象方法進行重寫
7、介面
介面的作用類似於抽象類,不同點在於介面中的所有方法和屬性都是沒有實值的,換句話說介面中的所有方法都是抽象方法。介面主要負責定義一個類的結構,介面可以去限制一個對象的介面,對象只有包含介面中定義的所有屬性和方法時才能匹配介面。同時,可以讓一個類去實現介面,實現介面時類中要保護介面中的所有屬性。
-
示例(檢查對象類型):
-
interface Person{ name: string; sayHello():void; } function fn(per: Person){ per.sayHello(); } fn({name:'孫悟空', sayHello() {console.log(`Hello, 我是 ${this.name}`)}});
-
-
示例(實現)
-
interface Person{ name: string; sayHello():void; } class Student implements Person{ constructor(public name: string) { } sayHello() { console.log('大家好,我是'+this.name); } }
-
8、屬性的封裝
- public
- 修飾的屬性可以再任意位置訪問修改默認值
- private
- 私有屬性,私有屬性只能在類內部進行訪問修改
- protected
- protected受包含的屬性,只能在當前類和當前類的子類中訪問
- getter方法用來讀取屬性
- setter方法迎來設置屬性
- getter和setter被統一稱為屬性的存儲器,定義時在方法之前添加get和set,調用的時候直接通過點語法調用
(function (){
// 定義一個表示人的類
class Person{
// TS可以在屬性前添加屬性的修飾符
/*
* public 修飾的屬性可以在任意位置訪問(修改) 默認值
* private 私有屬性,私有屬性只能在類內部進行訪問(修改)
* - 通過在類中添加方法使得私有屬性可以被外部訪問
* protected 受包含的屬性,只能在當前類和當前類的子類中訪問(修改)
*
* */
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
/*
* getter方法用來讀取屬性
* setter方法用來設置屬性
* - 它們被稱為屬性的存取器
* */
// TS中設置getter方法的方式
get name(){
// console.log('get name()執行了!!');
return this._name;
}
set name(value){
this._name = value;
}
get age(){
return this._age;
}
set age(value){
if(value >= 0){
this._age = value
}
}
}
const per = new Person('孫悟空', 18);
/*
* 現在屬性是在對象中設置的,屬性可以任意的被修改,
* 屬性可以任意被修改將會導致對象中的數據變得非常不安全
* */
// per.setName('豬八戒');
// per.setAge(-33);
per.name = '豬八戒';
per.age = -33;
// console.log(per);
class A{
//protected是保護的屬性,只能在當前類和子類中設置
//protected只能在當前類和當前類的子類中設置
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{
name: string;
age: number
// 可以直接將屬性定義在構造函數中
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}*/
class C{
// 直接將屬性定義在構造函數中
constructor(public name: string, public age: number) {
}
}
const c = new C('xxx', 111);
console.log(c);
})();
9、泛型
定義一個函數或類時,有些情況下無法確定其中要使用的具體類型(返回值、屬性、參數的類型不能確定),此時泛型便能通夠發揮作用
-
function test(arg:any):any{ return arg }
-
在這個例子中,test函數又一個參數類型不確定,但是能確定的是其返回值的類型和參數的類型是相同的,由於類型不確定所有參數和返回值均使用了any,但是很明顯這樣做是不合適的,首先使用any會關閉TS的類型檢查,其次這樣設置也不能體現出參數和返回值是相同的類型
-
通過泛型來確認參數和返回值的類型相同
-
function test<T>(arg:T):T{ return arg }
-
這裡的
<T>
就是泛型,T是我們給這個類型起的名字(不一定必須叫T),設置泛型後即可在函數中使用T來表示該類型。所以泛型其實很好理解,就表示某個類型
-
那麼如何使用上面的函數呢?
-
方式一(直接使用)
-
test(10)//直接使用
-
使用時直接傳遞參數使用,類型會由TS自動推斷出來,但有時編譯器無法自動判斷時還需要使用下面的方式
-
方式二(指定類型)
-
也可以在函數後手動指定泛型
-
-
可以同時指定多個泛型,泛型間使用逗號隔開
-
function test<T,K>(a:T,B:K):K { return b } test<number,string>(10,"hello")
-
使用泛型時,完全可以將泛型當成是一個普通的類去使用
-
-
類中同樣可以使用泛型
-
class MyClass<T>{ prop:T constructor(prop:T){ this.prop = prop } }
-
-
除此之外,也可以對泛型的範圍進行約束
-
interface MyInter{ length: number; } function test<T extends MyInter>(arg: T): number{ return arg.length; }
-
使用T extends MyInter表示泛型T必須是MyInter的子類,不一定非要使用介面類和抽象類同樣適用。
-
-