class
ts是物件导向语言,因此要好好利用它,就是要将相关联的变数、方法都群组在一起,也就是使用class。
延续座标的例子,由于变数x, y及方法drawPoint、getDistance都是与Point有关,因此创造一个名为Point的class来放这些东西。
class Point{ x:number, //称为field y:number, draw(){ //称为method console.log('x:',this.x,' y:',this.y); } getDistance(){ //... }}
有了名为Point的class后,就可以建立一个Point的instance(中文要叫甚么呢?实体吗?),就叫做point1吧,并且使用Point有的方法了
let point1 = new Point; // point1就是依据class Point建立的instancepoint1.draw(); //x:undefine,y:undefine (´⊙ω⊙`)
不过,在定义class时设定了fields x & y和method draw,可是我们没有给x, y值,也没有限制一定要给值,为了避免错误,就有了constructor
Constructor
在定义class Point时加入constructor,于是每当建立Point的instance时,就可以在()内给予所需的参数们。
class Point{ x:number, //称为field y:number, constructor(x:number, y:number){ this.x = x; this.y = y; } draw(){ //称为method console.log('x:',this.x,' y:',this.y); }}
有了constructor后,我们要创建instance时,如果不给参数就会有错误讯息,有那么一些些防呆的效果
let point2 = new Point(8,24);point2.draw(); //x:8,y:24 ヽ(✿゚▽゚)ノ
不过当然,人生就是有时候需要给参数、有时候又不用,所以在建立constructor时,加上一个问号?,生命就会多一点弹性:
class Point{ x:number, //称为field y:number, constructor(x?:number, y?:number){ this.x = x; this.y = y; } draw(){ //称为method console.log('x:',this.x,' y:',this.y); } getDistance(){ //... }}let point2 = new Point();
有了?后,建立instance时没给参数,typescript也不会生气了。
access modifier
当我们好不容易透过Point 创立了point2,它的座标应该在(8,24),可是当时光飞逝,有一天point2.x和point2.y可能不小心被改掉,于是point2已经不是当初我认识的那个point2了
let point2 = new Point(8,24);point2.x=4;point2.y=12;point2.draw(); //x:4,y:12 Σ(;゚д゚)
如果不希望它改变,就需要在定义class时,将x & y 加入private
class Point{ private x:number, //用private把x & y藏得好好的 private y:number, constructor(x:number, y:number){ this.x = x; this.y = y; } draw(){ //称为method console.log('x:',this.x,' y:',this.y); }}let point2 = new Point(8,24);point2.x=4; //错误讯息point2.y=12; //错误讯息point2.draw(); //x:8,y:24 ヽ(✿゚▽゚)ノ
只有在建立instance时,可在()内给予参数,之后就不能改变了。
做到这里,不觉得我们先定义了x ,y的型别、又在constructor再次指定x,y,总觉得很多余啊,所以,当然就可以简化啦~直接在constructor的参数中给予public或private,ts就会知道这两位是此class的field了。
class Point{ constructor(public x:number,private y:number){ } draw (){ //method console.log('x:',this.x,' y:',this.y); }}
可是可是,用了private后,一旦建立一个Point并初始化x,y,一切就无法挽回、不能改变了吗?
不不不,还是有方法的,在class Point多新增一个修改x,y的方法method就好(人类可真矛盾啊),好处就在于,此时就可以设定条件
,符合条件在能修改成功啰。
class Point{ constructor(public x:number,private y:number){ } draw (){ //method console.log('x:',this.x,' y:',this.y); } getX(){ //呼叫这个方法就能知道x值是甚么 return this.x; } setX(value){ //呼叫此方法,设定value只要是大于0,就能改变x值更新成value。 if(value <= 0){ throw new Error('X不能小于0'); }else this.x = value; }}
TS当然也知道大家有这种需求,所以其实get与set是个将X设为Point的property的语法。
get X(){ //称作getter return this.x; } set X(value){ //称作setter if(value <= 0){ throw new Error('X不能小于0'); }else this.x = value; }
有了get,就可以用point2.X
来取得this.x的值,.X
成为了point2的property。
有了set,就可以用point2.X = 4
来将this.x的值改为4,使用set,此property才能=某值喔。
当然,在这只是为了取得及重新设定座标x,才将property的名称设为X,如果想设计其他property给Point,也是可以的啦
不过,明明我们给的field是小写x,但当作property取得时却是大写X,这当然是因为如果都用小写,就是重複名称的变数了,但在命名原则中,只有特殊如class才能使用首字大写,只是变数当然都用驼峰命名,于是依照惯例,像这种private变数,都会在前加底线_、例如_x,那么设定对应property时,就可以用小写x作为property名称,使用上就比较不会搞混了
class Point{ constructor(private _x:number,private _y:number){ } draw (){ //method console.log('x:',this._x,' y:',this._y); } get x(){ return this._x; } set x(value){ if(value <= 0){ throw new Error('X不能小于0'); }else this._x = value; }}let point = new Point(1,2);point.x; //使用getterpoint.x = 10; //使用setter
Module
身为物件导向语言,最喜欢一个积木、一个积木、最后组成城堡,当Point这块积木常常被拿来盖城堡,就会将它设计成量产的积木,而不是每一份城堡.ts文件中,都有一段一模一样的class Point定义。
于是,新建一个叫做point.ts的档案,将其export;在欲使用Point的ts档案中import进去,就能达成此目的
//point.ts档export class Point{ constructor(private _x:number,private _y:number){ } draw (){ //method console.log('x:',this._x,' y:',this._y); } get x(){ return this._x; } set x(value){ if(value <= 0){ throw new Error('X不能小于0'); }else this._x = value; }}
//欲使用Point的ts档import { Point } from './point'; //from相对路径,后面没有.ts喔let point = new Point(1,2);point.x; //使用getterpoint.x = 10; //使用setter