typescript[3]-class

来源:互联网 发布:火星网络加速器 编辑:程序博客网 时间:2024/04/27 16:32
// -------- Classes(类) --------// 传统的javascript依赖在函数和原型链(prototype)作为基础概念来构建可重用的部件// 但是对于习惯了面对对象编程(class继承功能,对象由class来生成)的程序员来说,还是有点麻烦// 从ES6开始,下一个版本的javascript,js程序员也可以使用面对对象编程// 在typescript,我们允许开发者现在就使用这些技术,然后把他们编译成javascript,在各个浏览器和平台上运行,而不用等待下一个版本的javascript// 让我们来看下基于class的例子class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello," + this.greeting;}}var greeter = new Greeter("world");// 如果你用过c#或者java,你会觉得语法很类似.我们声明一个叫"Greeter"的新class.// 这个class有3个成员,分别是一个叫"greeting"的属性,一个构造函数和一个'greet'的方法// 你会发现在class里,当我们引用一个成员的时候,会在前面加上'this'.这表示一个成员操作// 在最后一行,我们使用'new'关键字来构造一个Greet的实例.// 它会调用我们早些定义的constructor,创建一个Greet的'Shape'的对象,然后用constructor初始化它// -------- Inheritance -------- // 在typescript中,我们可以使用通用的面对对象模式.// 面对对象编程中,一个根本模式是通过继承,从一个已有的class生成一个新的class// 例子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(meter = 45) {alert("Galloping");super.move(meter);}}var sam = new Snake("sammy the python");var tom: Animal = new Horse("tommy the palomino");sam.move();sam.move(34);// 这个例子涵盖了相当多的typescript的继承特性,这些特性也在其他语言中非常通用// 这里我们使用'extends'关键字来建立subclass(子类)// 你可以看到'Horse'和'Snake'从'Animal'这个class里获取它的特性// 这个例子也展示了如何'覆盖'(override)父类的方法,而后在子类里重写// 'Snake'和'Horse'建立了个move方法来重写父类'Animal'方法,从而给与各自class以特定功能//  -------- Private/Public modifiers(私有/公有 修饰符) -------- // 默认是public// 你可能已经注意到,我们没有使用'public'关键字来使class的成员可见(向外暴露)// 类似在c#语言中,class的成员如果要可见,则必须显式的用上'public'这个标签来修饰// 而在typescript中,默认每个成员就是public// 你依旧可以标记成员为private,这样你就控制class的什么成员是对外可见.// 我们可以这样重写Animal类class Animal2 {    private name: string;    constructor(theName: string) { this.name = theName; }    move(meters: number) {        alert(this.name + " moved " + meters + "m.");    }}// -------- Understanding private(理解私有) -------- // typescript是个结构化类型的系统// 当我们比较2个不同的类型,不考虑他们来自哪里,只要他们的类型是compatible(兼容的),我们就说他们是compatible// 当我们比较带有'private'标签的成员的类型时候,我们不同对待// 两个类型被认为是compatible,如果其中一个有private成员,那么另一个必须有同样声明的private成员// 例子class Animal3 {private name: string;constructor(theName: string) { this.name = theName; }}class Rhino extends Animal3 {constructor() { super("Rhino"); }}class Employee {private name: string;constructor(theName: string) { this.name = theName; }}var animal = new Animal3("Goat");var rhino = new Rhino();var employee = new Employee("Bob");animal = rhino;// animal = employee; //error: Animal and Employee are not compatible// 在这个例子里,我们有2个类'Animal'和'Rhino',其中'Rhino'是'Animal'的子类.// 我们还有一个看上去跟'Animal'完全一致的class'Employee'// 我们创建他们的实例,来相互赋值,看看会发生什么// 'Animal'跟'Rhino'共享了他们在'Animal'中定义的私有变量name,所以他们是兼容的// 但是,这不适用于'Employee'// 当我们想把从'Employee'中生成的实例赋予'Animal'类型变量时,我们得到'不可兼容'的错误// 即便'Employee'有着跟'Animal'一样的私有成员name,但是他不是一个从'Animal'生成的// -------- Parameter properties(参数属性) -------- // 使用public和private关键字来修饰构造函数的参数,可以一步定义和初始化类成员// 这里有对上述例子的重构// 请注意, 通过'private name:string',将这个'name'运用到构造函数里, 'theName'被简化了class Animal5 {    constructor(private name: string) { }    move(meters: number) {        alert(this.name + " moved " + meters + "m.");    }}// 这样的方式使用private关键字就生成一个private成员,public同理// -------- Accessors(访问器) -------- // typescript支持getters/setters来操作对象成员,这提供了更好的细粒度操作// 让我们通过一个简单的class来过以下get和set// 首先让我们来看一个没有get和set的例子class Employee2 {    fullName: string;}var employee2 = new Employee2();employee2.fullName = "Bob Smith";if (employee2.fullName) {    alert(employee2.fullName);}// 这样让人们直接的设置'fullName'固然便捷,但是人们随意改变值也会带来麻烦// 在这个版本里,我们会检测密码当他们想修改值的时候// 我们通过set来添加了检测密码的功能,从而改变直接设置'fullName'这种方式// 我们增加了get,让上述例子工作更天衣无缝var passcode = "secret passcode";class Employee3 {    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 employee3 = new Employee3();employee3.fullName = "Bob Smith";if (employee3.fullName) {    alert(employee3.fullName);}/*编译结果var Employee3 = (function () {    function Employee3() {    }    Object.defineProperty(Employee3.prototype, "fullName", {        get: function () {            return this._fullName;        },        set: function (newName) {            if (passcode && passcode == "secret passcode") {                this._fullName = newName;            }            else {                alert("Error: Unauthorized update of employee!");            }        },        enumerable: true,        configurable: true    });    return Employee3;})();*/// 要验证我们的访问器是不是在检测密码,我们可以修改passcode变量使之不匹配,从而应该看到警告框提醒我们没有修改employee// 备注:访问器的实现需要你的js编译器不低于ES5// 笔者:typescript默认js编译器为ES3,所以编译时候也写成 tsc class.ts -t ES5// -------- Static Properties(静态属性) -------- // 目前为止,我们只讨论了实例对象的实例成员// 我们也可以建立class的静态成员,静态成员对class可见,而不是实例(即只能用class去访问)// 这个例子里,我们用'static'来修饰origin成员,这样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);var grid2 = new Grid(5.0);alert(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));alert(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));// -------- Advanced Techniques(进阶) --------// 当你定义了一个typescript的class,你也同时做了许多声明// 第一个就是class的实例的类型class Greeter1 {    greeting: string;    constructor(message: string) {        this.greeting = message;    }    greet() {        return "Hello, " + this.greeting;    }}var greeter: Greeter1;greeter = new Greeter1("world");alert(greeter.greet());// 这里,当我们说'var greeter: Greeter',我们在用'Greeter'作为Greeter这个类的实例的类型// 这对于面对对象编程的程序员来说,是第二天性// 我们也用'constructor'函数去创建了另外一个值// 当我们new出一个class的实例的时候,这个函数就会被调用// 看看在实战它会成为个什么样子,让我们看看上述代码生成的js代码/*    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'将被指向constructor函数// 当我们调用new,来执行这个函数的时候,我们得到这个class的实例// 这个constructor函数也包含了所有这个class的静态成员// 另一种来考虑class的方式是,class包含了它的实例部分和静态部分// 让我们修改下例子,来展示差别class Greeter2 {    static standardGreeting = "Hello, there";    greeting: string;    greet() {        if (this.greeting) {            return "Hello, " + this.greeting;        }        else {            return Greeter2.standardGreeting;        }    }}var greeter1: Greeter2;greeter1 = new Greeter2();alert(greeter1.greet());var greeterMaker: typeof Greeter2 = Greeter2;greeterMaker.standardGreeting = "Hey there!";var greeter2:Greeter2 = new greeterMaker();alert(greeter2.greet());// 这个例子里,'greeter1'跟我们前面实例化'Greet'一样,使用this这个我们前面见过的object// 接下来,我们直接使用这个class// 这里我们创建一个变量'greeterMaker',这个变量指向class自己,或者说指向它的constructor// 这里,我们使用'typeof Greeter',意思是给我Greeter这个class的类型,而不是实例的类型// 或者更加精确的说,'给我Greeter的类型',也就是constructor函数的类型// 这个类型包括了Greeter类的所有静态成员连同所有用constructor生成的实例成员// 我们new一个greeterMaker来展示// -------- Using a class as an interface(把类当作接口用) --------// 如我们在上一节说的,class定义做了两件事情// 1 一个用来代表class实例的类型// 2 一个constructor函数// 因为class创建了类型,所以你在用class的地方,你也可以使用接口来代替class Point {    x: number;    y: number;}interface Point3d extends Point {    z: number;}var point3d: Point3d = {x: 1, y: 2, z: 3};

0 0
原创粉丝点击