TypeScript入門 - 2

by vivid 3. 十月 2018 11:22

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N181020001
出刊日期: 2018/10/03

在這篇文章中,將要介紹TypeScript函式(Function)的基本語法。TypeScript函式用來執行一些動作,函式可以有參數,函式可以有回傳值。通常函式會傳回一些計算或比較的結果給呼叫端。

函式(Function)可以將一到多行程式碼封裝在一起,使用一個名稱做識別。這樣可以讓您簡化程式碼,也容易做重複使用。

 

函式(Functions)

在TypeScript中通常會將函式(Functions)定義在類別(Class)之中,以方法(Method)的型式存在,透過類別將程式碼組織成易重複使用的單位。宣告函式的語法如下:

function 函式名稱(參數0:型別, 參數1:型別... 參數N:型別): 回傳型別 {

    return x + y;

}

以下定義一個「add」函式,包含「x」、「y」兩個參數,參數名稱後方都標示了型別(type annotation)為數值型別:

function add(x: number, y: number): number {

    return x + y;

}

var r = add(10, 20);

console.log(r)

範例中叫用「add」函式時,傳入「10」、「20」引數,若呼叫函式傳入錯誤型別的引數,編譯程式時將會回報錯誤,例如以下範例程式碼:

var r = add('10', true); // TSError

「add」函式的回傳值型別也是「number」型別,因此若回傳錯誤型別的資料,在編譯程式時將會回報錯誤,參考以下範例程式碼:

function add(x: number, y: number): number {

    return true;

}

函式回傳值

若函式沒有回傳值,則可以省略掉函式 ( ) 符號後方的回傳型別標註(type annotation),例如以下範例程式碼:

function add(x: number, y: number) {

    console.log(x + y);

}

add(10, 20);

或者也可以使用「void」關鍵字來指明函式沒有回傳值,例如以下範例程式碼:

function add(x: number, y: number): void {

    console.log(x + y);

}

add(10, 20);

使用「void」關鍵字的好處是,可以防止函式中撰寫了試圖回傳資料的程式碼,例如以下程式碼將發生錯誤:

function add(x: number, y: number): void {

    console.log(x + y);

    return x + y; // error

}

add(10, 20);

除了明確指定函式參數型別與回傳值型別之外,你也可以利用自動型別推論功能來自動識別型別。例如以下範例程式碼,使用了「any」關鍵字來達到型別推論功能,叫用「add」函式時,若傳入兩個數值,則進行數值相加;若傳入兩個字串,則進行字串串接。傳入一個數值,一個字串,則進行字串串接;最後傳入兩個布林值,會轉型成數值,然後做數值相加:

function add(x: any, y: any): any {

    return x + y;

}

var r = add(10, 20); // 30,數值相加

console.log(r)

var r = add('10', '20'); // 1020,字串串接

console.log(r)

var r = add('10', 20); // 1020,字串串接

console.log(r)

var r = add(true, false); // 1,數值相加

console.log(r)

選擇性參數(Optional Parameter)

在TypeScript中,若叫用函式時,沒有傳遞引數到必要參數,則編譯階段便會發生錯誤,例如以下範例程式碼:

function add(x: number, y: number): number {

    return x + y;

}

var r = add(10); // Error

console.log(r)

選擇性參數(Optional Parameters)可以解決這個問題,只要在函式中的參數名稱後方,加上「?」符號,就可以當做選擇性參數來使用,參考以下範例程式碼,「y」是一個選擇性參數,第二次叫用「add」函式時,因為沒有傳遞引數到「y」參數,「y」參數的值便會是「undefined」,因此「x + y」的結果會是「NaN」,代表無效的數值運算:

function add(x: number, y?: number): number {

    return x + y;

}

var r = add(10, 20); // 30

console.log(r)

var r = add(10); // NaN

console.log(r)

若要避免產生無效的數值運算「NaN」,可以利用「typeof」運算子來進行判斷參數是否有傳值,參考以下範例程式碼,「typeof」運算子回傳的結果是字串,因此判斷式中的「'undefined'」使用單引號包起來:

function add(x: number, y?: number): number {
  var r: number;
  if (typeof y !== 'undefined') {
    r = x + y;
  } else {
    r = x;
  }
  return r;
}

var r = add(10, 20); // 30
console.log(r)

var r = add(10); // 10
console.log(r)


另外,一個函式的選擇性參數可以有多個,但它們必需出現在所有必要參數之後,以下程式碼將無法編譯執行,選擇性參數「y」出現在必要參數「x」之前:

function add(y?: number, x: number): number { // error

    console.log(y)

    return x + y;

}

預設參數(Default Parameter)

函式中的參數只要給與預設值,就可以視為預設參數(Default Parameter)。當叫用函式未傳值(引數)時,便可使用預設參數值來取代,參考以下範例程式碼,「y」的預設參數值為「0」:

function add(x: number, y: number = 0): number {

    return x + y;

}

var r = add(10, 20); // 30

console.log(r)

var r = add(10); // 10

console.log(r)

若將此段TypeScript程式碼轉譯成JavaScript程式碼,可以看到以下類似前述範例,在函式中包含一段手動檢查「y」參數是否有傳值的「if」判斷式,並適當設定「y」的預設值:

function add(x, y) {
    if (y === void 0) { y = 0; }
    return x + y;
}
var r = add(10, 20); // 30
console.log(r);
var r = add(10); // 10
console.log(r);

剩餘參數(Rest Parameter)

若函式定義了剩餘參數(Rest Parameter),那麼叫用函式時,便可以傳遞「0」到多個指定型別的引數(argument)。一個函式只能夠定義一個剩餘參數,它必需出現在所有必要參數之後,也就是參數列的最後一個。剩餘參數的型別必需是一個陣列型別。要定義剩餘參數,只要在參數名稱前方加上「...」運算子,參考以下範例程式碼,「sum」函式中包含一個剩餘參數「nums」,其型別是數值陣列:

function sum(...nums: number[]): string {
  let total = 0;
  for (let i = 0; i < nums.length; i++) {
    total += nums[i];
  }
  return `Total = ${total}`;
}

var r = sum(1, 3);
console.log(r) // Total = 4

var r = sum(1, 3, 4, 6, 6);
console.log(r) // Total = 20

var r = sum();
console.log(r) // Total = 0


叫用帶有剩餘參數的「sum」函式時,則可以傳入0至多個引數。

函式多載(Overloads)

在許多程式語言中,函式具備多載(Overloads)特性,只要函式的參數個數或型別不同,函式名稱可以命為相同,不過你需要實作多個同名的函式。而在TypeScript中,函式多載只有一個實作,參考以下範例程式碼,定義多載函式「add」,可以傳入兩個數值,或兩個字串參數:

function add(x: number, y: number): number;

function add(x: string, y: string): string;

function add(x: any, y: any): any {

    return x + y;

}

console.log( add(10, 20) ); // 30

console.log( add("10", "20") ); // 1020

console.log(add(10, 20 , 30)); // error

console.log(add(10)); // error

console.log( add(10, "20") ); // error

console.log( add(false,true) ); // error

var r:boolean = add(1,2); // error

前兩行程式碼先定義函式簽名,接著再實作「add」函式。定義函式簽名之後,叫用「add」函式時,若傳入的引數和函式簽名中的參數、與回傳值的型別不相符合時,便視之為錯誤。

 

箭頭函式(Arrow Functions)

箭頭函式(Arrow Functions)提供一個簡易的語法來定義函式,有些程式語言也有類似的語法,並稱之為Lambda Function。函式定義時省略掉「function」關鍵字,讓語法更簡潔,無需重複輸入「function」字串來定義函式。

箭頭函數(Arrow Function)的語法是由函式參數開始,跟隨著「=>」符號,「=>」符號右方則是函式程式碼。參考以下範例程式碼,若有一「func1」函式定義如下,不含參數,回傳一個數值:

function func1(): number {

    return 1;

}

console.log(func1())

改成箭頭函數寫法,此函數回傳「number」型別,參數部分使用小括號包起來,沒有參數的話,小括號不省略,「=>」符號後接運算式,不必撰寫「return」關鍵字來回傳值:

let func1 = (): number => 1;

console.log(func1())

若有一「func2」函式定義如下,包含一個數值型别參數「x」,回傳一個數值:

function func2(x: number): number {

    return 2 * x;

}

console.log(func2(10)) // 20

改成箭頭函數寫法如下,參數部分使用小括號包起來,「=>」符號後接運算式來回傳值:

let func2 = (x: number): number => 2 * x;

console.log(func2(10)) // 20

若有一「func3」函式定義如下,含「2」個數值型別參數,回傳一個數值:

function func3(x: number, y: number): number {

    return x + y;

}

console.log(func3(10, 20)) // 30

改成箭頭函數寫法如下,「=>」右方的程式碼若有多行,使用 「{ }」 符號包起來,並在函式中明確使用「return」關鍵字來回傳運算結果:

let func3 = (x: number, y: number): number => {

    return x + y;

}

console.log(func3(10, 20)) // 30

若有一「func4」函式定義如下,包含一個數值型別參數:

function func4( x : number ) :number {

    return x;

}

console.log(func4(10)) // 10

改成箭頭函數寫法如下,函式中程式碼若有多行,使用 「{ }」 符號包起來,並在函式中明確使用「return」關鍵字來回傳運算結果:

let func4 = (x :number):number => {

    return x;

};

console.log(func4(10)) // 10

若有一「func5」函式定義如下,包含二個參數,沒有回傳值:

let func5 = function (x: number, y: number) {

    console.log(x + y); // 30

}

func5(10, 20)

改成箭頭函數寫法如下:

let func5 = (x: number, y: number) => {

    console.log(x + y); // 30

}

func5(10, 20)

此外,若箭頭函式回傳物件,則物件要以 「( )」 號括起來,參考以下範例程式碼:

let func6 = (id: number, name: string) => ({ empId: id, empName: name });

let emp = func6(1, 'mary');

console.log(emp.empId) // 1

console.log(emp.empName) // mary

最後,箭頭函式會根據詞法作用域(Lexical Scope)來設定「this」,參考以下範例程式碼,「this」將指向函式回傳的新物件,參考以下範例程式碼:

let func7 = (id: number, name: string) => ({
  empId: id,
  empName: name,
  displayInfo: function () {
    setTimeout(() => console.log(this.empName), 0)
  }
});

let emp = func7(1, 'mary');
emp.displayInfo(); // mary

 

參考以下範例程式碼,若是使用標準的匿名函式語法,當「setTimeout」函式指定的時間到了,程式執行到「setTimeout」的回呼函式時,「this」已遺失,則會印出「undefined」:

var func7 = function (id, name) { return ({
    empId: id,
    empName: name,
    displayInfo: function () {
        setTimeout(function () {
            console.log(this.empName);
        }, 0);
    }
}); };
var emp = func7(1, 'mary');
emp.displayInfo(); // undefined

 

而TypeScript編譯器在編譯時,產生類似以下的程式碼,利用一個變數「_this」儲存this的值,後續程式碼將使用「_this」來參考到建立的新物件,自動維護「this」:

var func7 = function (id, name) { return ({
    empId: id,
    empName: name,
    displayInfo: function () {
        var _this = this;
        setTimeout(function () {
            console.log(_this.empName);
        }, 0);
    }
}); };
var emp = func7(1, 'mary');
emp.displayInfo(); // mary

Tags:

.NET Magazine國際中文電子雜誌 | TypeScript | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List