typescript[3]-class

// -------- 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值