iT邦幫忙

0

TypeScript 學習筆記

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建立的instance
point1.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; //使用getter
point.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; //使用getter
point.x = 10; //使用setter

尚未有邦友留言

立即登入留言