TypeScript Class(類)

来源:http://www.cnblogs.com/ys-ys/archive/2016/02/22/5208308.html
-Advertisement-
Play Games

傳統的JavaScript註重用函數和基於原型的繼承來創建可復用的組件,但這可能讓用習慣面對對象方式的程式員感到棘手,因為他們的繼承和創建對象都是由類而來的。從JavaScript的下一個版本,ECMAScript 6開始,JavaScript程式員就能夠用基於這種基於類的面對對象方式來創建編寫自己


傳統的JavaScript註重用函數和基於原型的繼承來創建可復用的組件,但這可能讓用習慣面對對象方式的程式員感到棘手,因為他們的繼承和創建對象都是由類而來的。從JavaScript的下一個版本,ECMAScript 6開始,JavaScript程式員就能夠用基於這種基於類的面對對象方式來創建編寫自己的程式了。在TypeScript中,不需要再等JavaScript的下一個版本就已經支持開發者使用這一技術了。


讓我們來看一個簡單的基於類的例子:

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
var greeter = new Greeter("world");

如果你之前有使用過C#或者Java,會覺得語法非常相似。我們聲明一個新的類"Greeter"。這個類裡面有三個成員,一個名為"greeting"的屬性,一個constructor和一個"greet"方法。
你會註意到,在類裡面當某一個成員使用了"this",意味著他訪問的是這個類的成員。
在最後一行中,我們使用"new"來為Greeter類構造一個實例。這將會調用之前定義的構造函數,並且創建一個新的Greeter類型的對象,並且執行構造函數來初始化這個對象。

繼承
在TypeScript中,我們可以使用常見的面向對象模式。當然,在基於類的編程中最基本的模式之一就是能夠創建一個新的類,這個新的類繼承已有的類,並對已有的類做擴展。
來看一個例子:

class Animal {
    name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number = 0) {
        alert(this.name + " moved " + meters + "m.");
    }
}
class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(meters = 5) {
        alert("Slithering...");
        super.move(meters);
    }
}
class Horse extends Animal {
    constructor(name: string) { super(name); }
    move(meters = 45) {
        alert("Galloping...");
        super.move(meters);
    }
}
var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);

這個例子包含了TypeScript中繼承的特性,當然,在其他語言中也一樣。在這裡,我們使用"extends"關鍵字來創建一個子類。你可以看到,這裡"Horse"和"Snake"兩個子類都基於"Animal"這個父類,並且對其特性進行了擴展。在這裡,我們使用"extends"關鍵字來創建一個子類。你可以看到,這裡"Horse"和"Snake"兩個子類都基於"Animal"這個基類並且獲取其特性。
例子也提現出在子類中可以重寫基類中的方法以達到重寫後的方法是在這個子類中專用。這裡的"Horse"和"Snake"都創建了"move"這個方法,這樣就重寫了從基類繼承過來的move方法,並且在不同類中給"move"不同的方法。

公有和私有的修飾符
預設是public(公有)
你可能已經註意到了,在上面的例子中,我們並未對類的任何可見成員使用"public"關鍵字進行修飾。類似C#語言,需要給成員使用"public"修飾符用來明確它是可見。在TypeScript中,每個成員預設是"public"的。
你還可以給成員標記上"private",這樣你就可以控制在你的類之外哪些成員是可見。我們可以像這樣重寫上一節的"Animal"類:

class Animal {
    private name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

理解Private(私有)
TypeScript是個構造類型的系統。當我們對兩個類型進行比較的時候,無論它們是從哪裡來,如果所有成員的類型都是相容的,那麼我們可以認為他們的類型也是相容的。
當我們比較的類型中含有"private"(私有)成員,則我們就需要不同的對待了。兩個類型(假如是A和B)被認為是相容的,如果A類型含有一個私有成員,那麼B類型就必須也有一個私有成員並且與A類型的私有成員源自同一處聲明。
讓我們用一個例子來更好的看看私有成員在實踐中如何運用:

class Animal {
    private name:string;
    constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
    constructor() { super("Rhino"); }
}
class Employee {
    private name:string;
    constructor(theName: string) { this.name = theName; }    
}
var animal = new Animal("Goat");
var rhino = new Rhino();
var employee = new Employee("Bob");
animal = rhino;
animal = employee; // 錯誤: Animal 和 Employee 不相容

在這個例子中,我們有一個"Animal"和一個"Rhino","Rhino"是"Animal"的一個子類。我們還有一個新的類"Employee",它看上去跟"Animal"類是完全相同的。我們給這些類分別創建實例,並且對他們進行相互賦值,看下將會發生什麼。因為"animal"和"rhino"的私有成員都是從"Animal"類定義的"private name: string"共用而來的,所以他們是相容的。然而,"employee"的情況卻不是這樣的。當我們試圖將"employee"賦值給"animal",我們得到了一個錯誤,他們的類型是不相容的。儘管"Employee"也有一個名稱是"name"的私有成員,但它和在"Animal"中的私有成員"name"還是不相同的。

參數屬性
關鍵字"public"和"private"通過創建參數屬性的方式給我們提供了創建和初始化類的成員的便捷方式。這個特性讓你可以一個步驟就創建和初始化成員。這裡有一個之前例子的進一步修改。註意我們是如何在constructor中將"name"使用"private name: string"的便捷方式完整的創建並初始化成這個類的私有成員"name"的。

class Animal {
    constructor(private name: string) { }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}
var goat = new Animal("Goat");
goat.move(25); // Goat moved 25 m.

通過這種方式使用"private"來創建和初始化私有成員,"public"也一樣。

訪問器
TypeScript提供 getters/setters 的方式來攔截對於對象成員的訪問。它讓我們可以更精確的控制如何對對象成員的進行訪問。
讓我們來將一個類改寫成用"get"和"set"。首先,我們從一個沒有"get"和"set"的例子開始:

class Employee {
    fullName: string;
}
var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}

以上代碼允許我們隨意設置fullName,可能我們會覺得這樣比較直接和方便,但這麼隨心所欲的改變名字也可能會導致問題。
在這個版本中,我們將給被允許修改員工信息的用戶一個可用的密碼。在對fullName進行"set"訪問的之前,我們會以檢查密碼來代替允許直接修改。我們添加一個相應的"get"讓之前的例子依然能實現。

var passcode = "secret passcode";
class Employee {
    private _fullName: string;
    get fullName(): string {
        return this._fullName;
    }
    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            alert("Error: Unauthorized update of employee!");
        }
    }
}
var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}

為了證明現在訪問需要密碼,我們可以修改密碼,然後我們會發現,當密碼不符合的時候會彈出提示"Error: Unauthorized update of employee!"(錯誤:沒有修改employee的許可權)。

註意:訪問器需要我們將文件以ECMAScript5編程輸出。

tsc --target ES5 your.ts

靜態屬性
到此為止,我們值談到類的實例成員,那些只有實例化後才初始化並且顯示的成員。我們還可以為類的創建靜態成員,那些在類本身可見而非實在實例上可見。在這個例子中,我們使用"static"來修飾"origin",因為他是所有Grid都會用到的東西。每個實例想要訪問這個屬性,都需要在前面加上類名。這就像要在實例前面加上"this"來訪問這個實例,這裡我們將使用"Grid."來訪問靜態屬性。

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        var xDist = (point.x - Grid.origin.x);
        var yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}
var grid1 = new Grid(1.0);  // 1x 規模
var grid2 = new Grid(5.0);  // 5x 規模
alert(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
alert(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

高級技巧
構造函數
當你在TypeScript中聲明一個類的同時,你也定義了很多東西。首先就是這個類的實例類型。

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
var greeter: Greeter;
greeter = new Greeter("world");
alert(greeter.greet());

這裡,當我們寫"var greeter: Greeter",我們就已經將"Greeter"類的實例類型定義為"Greeter"了。這對於用過其它面向對象語言的程式員而言已經習以為常了。
我們也同時的創建了一個稱為構造函數的值,當我們使用"new"來為類創建實例的時候,我們將會調用這個函數。讓我們結合實踐,在編譯後的JavaScript中看看上面的這個例子吧:

var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();
var greeter;
greeter = new Greeter("world");
alert(greeter.greet());

在這裡,"var Greeter"是指定構造函數。當我們使用"new"並且執行這個函數之後,便會得到一個類的實例。這個構造函數包含了類的所有的靜態成員。換種說法,即類有靜態部分和實例部分。
讓我們稍微修改下例子看看它們的不同之處:

class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}
var greeter1: Greeter;
greeter1 = new Greeter();
alert(greeter1.greet());
// 上下代碼效果做對比
var greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
var greeter2:Greeter = new greeterMaker();
alert(greeter2.greet());

在這個例子中,"greeter1"和之前例子是一樣的。我們實例化了"Greeter"類,並且使用這個對象。結果也和之前的例子一樣。
接下來,我們直接使用這個類,我們創建了一個名為"greeterMaker"的新變數。這個變數保存了這個類,換種說法即保存了這個構造函數。這裡我們使用"typeof Greeter",這麼做的話"greeterMaker"的類型就成了"Greeter"類的類型,而非"Greeter"的實例的類型("Greeter"類的實例類型為"Greeter")。更準確的說,"給我Greeter類的類型",也就是構造函數的類型。這個類包含"Greeter"類的所有靜態成員和創建"Greeter"類的實例的構造函數。同之前的例子一樣,我們對"greeterMaker"使用"new",用來創建"Greeter"的實例並且觸發。

將類當作介面一樣使用
正如我們在上一節所說的,聲明一個類的同時會創建其他兩個東西:這個類的實例類型和一個構造函數。因為類能夠創建類型,所以在使用interface(介面)的地方都可以使用class(類)。

class Point {
    x: number;
    y: number;
}
interface Point3d extends Point {
    z: number;
}
var point3d: Point3d = {x: 1, y: 2, z: 3};

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 效果圖如下: 代碼如下: 1 <!DOCTYPE html> 2 <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 3 <meta charset="UTF-8"> 4 <title>Doc
  • 基於jQuery拖拽插件製作拖拽排序特效是一款非常實用的滑鼠拖拽佈局插件。效果圖如下: 線上預覽 源碼下載 實現的代碼。 html代碼: <h1>水平拖拽</h1> <div class="demo"> <div class="item item1"><span>1</span></div> <di
  • 引言: 最近遇到個問題,同時引用了jquery庫和另外一個js庫。當用$XX去調用js庫函數時,發現失效了!於是找資料,原來是jquery命名衝突了。因為許多 JavaScript 庫使用 $ 作為函數或變數名,jquery也一樣。其實$只是jquery的一個別名而已,假如我們需要使用 jquery
  • 一、工廠模式 function createStudent(name,age){ var o=new Object(); o.name=name; o.age=age; o.myName=function(){ alert(this.name); }; return o; } var student
  • 紅色:熱情、奔放、喜悅、莊嚴 黃色:高貴、富有、燦爛、活潑 黑色:嚴肅、夜晚、沉著 白色:純潔、簡單、潔凈 藍色:天空、清爽、科技 綠色:植物、生命、生機 灰色:莊重、沉穩 紫色:爛漫、富貴 棕色:大地、厚朴
  • 怎樣把一個DIV放到另一個div右下角??? 藉助CSS定位來實現,你將右下角的那個DIV放在另一個DIV裡面,參考代碼如下示: <div id="box1"> <div id="box2">測試內容</div> </div> <style> <!-- #box1{width:600px;heigh
  • [1]關鍵字 [2]16進位 [3]rgb [4]rgba [5]hsl [6]hsla
  • HTML 用兩個空格來代替製表符(tab) -- 這是唯一能保證在所有環境下獲得一致展現的方法。 嵌套元素應當縮進一次(即兩個空格) 對於屬性的定義,確保全部使用雙引號,絕不要使用單引號。 不要在自閉合(self-closing)元素的尾部添加斜線 -- HTML5 規範中明確說明這是可選的。 不要
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...