設計泛型的關鍵動機是在成員之間提供有意義的類型約束哟沫,這些成員可以是類的實例成員饺蔑、類的方法、函數的參數嗜诀、函數的返回值猾警。
- 動機和示例
下面是在JavaScript/TypeScript中,對一個先進先出的數據結構——隊列的簡單實現(xiàn)
class Queue {
private data = [];
push = item => this.data.push(item);
pop = () => this.data.shift();
}
在上述代碼中存在一個問題隆敢,它允許你向隊列中添加任何類型的數據发皿,當然,被彈出隊列的數據也可以是任意類型拂蝎。在下面的示例中穴墅,看起來人們可以向隊列中添加string類型的數據,但是實際上温自,該用法假定只有number類型會被添加到隊列里玄货。
class Queue {
private data = [];
push = item => this.data.push(item);
pop = () => this.data.shift();
}
const queue = new Queue ();
queue.push(0);
queue.push('1'); //因為不符合假定條件將會導致報錯
一個解決辦法(事實上,這也是不支持泛型的唯一解決辦法)是為這些約束創(chuàng)建的特殊類悼泌,如快速創(chuàng)建數字類型的隊列松捉。
class QueueNumber {
private data = [];
push = (item : number) => this.data.push(item);
pop = () : number => this.data.shift();
}
const queue = new QueueNumber ();
queue.push(0);
queue.push('1'); //錯誤:不能放入一個string類型的數據,只能是number類型的
//如果該錯誤得到修復馆里,其他將不會出現(xiàn)問題
當然隘世,這很快就會變得讓人痛苦。例如你想創(chuàng)建一個字符串類型的隊列鸠踪,你將不得不再次大幅度的修改代碼丙者。如果你想要一種方式去實現(xiàn),無論什么類型的數據被放進隊列中营密,被彈出的類型斗魚放入的類型一樣械媒,你可以使用泛型,這將讓事情變得很簡單卵贱。
示例如下
//創(chuàng)建一個泛型類
class Queue<T> {
private data :T[] = [];
push = (item : T) => this.data.push(item);
pop = () : T | undefined => this.data.shift();
}
//簡單用例
const queue = new Queue<number>();
queue.push(0);
queue.push('1');//錯誤滥沫,不能放入一個string類型的數據侣集,只能是number類型的键俱。
另外一個我們見過的例子是 reverse 函數,在下面的代碼中世分,在reverse 函數里提供了對函數參數表和函數返回值的約束编振。
function reverse<T>(items : T[]) : T[] {
const toreturn = [];
for (let i = items.length - 1; i >= 0; i--){
toreturn.push(items[i]);
}
return toreturn;
}
const sample = [1, 2, 3];
let reversed = reverse(sample);
reversed[0] = '1'; //錯誤
reversed = ['1', '2'] //錯誤
reversed[0] = 1; //正確
reversed = [1, 2] //正確
在本節(jié)中,你已經了解了在類和函數上使用泛型的例子。值得補充一點的事踪央,你可以為創(chuàng)建的成員函數添加泛型臀玄。
class Utility {
reverse<T>(items: T[]) : T[] {
const toreturn = [];
for (let i = items.length; i >= 0; i--) {
toreturn.push(items[i]);
}
return toreturn;
}
}
建議:你可以隨意調用泛型的參數,當你使用簡單泛型時畅蹂,泛型常用 T健无、U、V表示液斜。如果在你的參數里累贤,擁有不止一個泛型,你應該使用一個更語義化的名稱少漆,如TKey和TValue臼膏。依照慣例,以T作為泛型的前綴示损,在其他語言渗磅,如C++,這也被稱為模板检访。
- 看完再補充~~~