99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

TypeScript 函數(shù)

2022-04-21 09:20 更新

函數(shù)是JavaScript應(yīng)用程序的基礎(chǔ)。 它幫助你實現(xiàn)抽象層,模擬類,信息隱藏和模塊。 在TypeScript里,雖然已經(jīng)支持類,命名空間和模塊,但函數(shù)仍然是主要的定義 行為的地方。 TypeScript為JavaScript函數(shù)添加了額外的功能,讓我們可以更容易地使用。

函數(shù)

和JavaScript一樣,TypeScript函數(shù)可以創(chuàng)建有名字的函數(shù)和匿名函數(shù)。 你可以隨意選擇適合應(yīng)用程序的方式,不論是定義一系列API函數(shù)還是只使用一次的函數(shù)。

通過下面的例子可以迅速回想起這兩種JavaScript中的函數(shù):

// Named function
function add(x, y) {
    return x + y;
}

// Anonymous function
let myAdd = function(x, y) { return x + y; };

在JavaScript里,函數(shù)可以使用函數(shù)體外部的變量。 當(dāng)函數(shù)這么做時,我們說它‘捕獲’了這些變量。 至于為什么可以這樣做以及其中的利弊超出了本文的范圍,但是深刻理解這個機制對學(xué)習(xí)JavaScript和TypeScript會很有幫助。

let z = 100;

function addToZ(x, y) {
    return x + y + z;
}

函數(shù)類型

為函數(shù)定義類型

讓我們?yōu)樯厦婺莻€函數(shù)添加類型:

function add(x: number, y: number): number {
    return x + y;
}

let myAdd = function(x: number, y: number): number { return x+y; };

我們可以給每個參數(shù)添加類型之后再為函數(shù)本身添加返回值類型。 TypeScript能夠根據(jù)返回語句自動推斷出返回值類型,因此我們通常省略它。

書寫完整函數(shù)類型

現(xiàn)在我們已經(jīng)為函數(shù)指定了類型,下面讓我們寫出函數(shù)的完整類型。

let myAdd: (x:number, y:number)=>number =
    function(x: number, y: number): number { return x+y; };

函數(shù)類型包含兩部分:參數(shù)類型和返回值類型。 當(dāng)寫出完整函數(shù)類型的時候,這兩部分都是需要的。 我們以參數(shù)列表的形式寫出參數(shù)類型,為每個參數(shù)指定一個名字和類型。 這個名字只是為了增加可讀性。 我們也可以這么寫:

let myAdd: (baseValue:number, increment:number) => number =
    function(x: number, y: number): number { return x + y; };

只要參數(shù)類型是匹配的,那么就認為它是有效的函數(shù)類型,而不在乎參數(shù)名是否正確。

第二部分是返回值類型。 對于返回值,我們在函數(shù)和返回值類型之前使用( =>)符號,使之清晰明了。 如之前提到的,返回值類型是函數(shù)類型的必要部分,如果函數(shù)沒有返回任何值,你也必須指定返回值類型為 void而不能留空。

函數(shù)的類型只是由參數(shù)類型和返回值組成的。 函數(shù)中使用的捕獲變量不會體現(xiàn)在類型里。 實際上,這些變量是函數(shù)的隱藏狀態(tài)并不是組成API的一部分。

推斷類型

嘗試這個例子的時候,你會發(fā)現(xiàn)如果你在賦值語句的一邊指定了類型但是另一邊沒有類型的話,TypeScript編譯器會自動識別出類型:

// myAdd has the full function type
let myAdd = function(x: number, y: number): number { return x + y; };

// The parameters `x` and `y` have the type number
let myAdd: (baseValue:number, increment:number) => number =
    function(x, y) { return x + y; };

這叫做“按上下文歸類”,是類型推論的一種。 它幫助我們更好地為程序指定類型。

可選參數(shù)和默認參數(shù)

TypeScript里的每個函數(shù)參數(shù)都是必須的。 這不是指不能傳遞 nullundefined作為參數(shù),而是說編譯器檢查用戶是否為每個參數(shù)都傳入了值。 編譯器還會假設(shè)只有這些參數(shù)會被傳遞進函數(shù)。 簡短地說,傳遞給一個函數(shù)的參數(shù)個數(shù)必須與函數(shù)期望的參數(shù)個數(shù)一致。

function buildName(firstName: string, lastName: string) {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // ah, just right

JavaScript里,每個參數(shù)都是可選的,可傳可不傳。 沒傳參的時候,它的值就是undefined。 在TypeScript里我們可以在參數(shù)名旁使用 ?實現(xiàn)可選參數(shù)的功能。 比如,我們想讓last name是可選的:

function buildName(firstName: string, lastName?: string) {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
}

let result1 = buildName("Bob");  // works correctly now
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");  // ah, just right

可選參數(shù)必須跟在必須參數(shù)后面。 如果上例我們想讓first name是可選的,那么就必須調(diào)整它們的位置,把first name放在后面。

在TypeScript里,我們也可以為參數(shù)提供一個默認值當(dāng)用戶沒有傳遞這個參數(shù)或傳遞的值是undefined時。 它們叫做有默認初始化值的參數(shù)。 讓我們修改上例,把last name的默認值設(shè)置為"Smith"。

function buildName(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined);       // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result4 = buildName("Bob", "Adams");         // ah, just right

在所有必須參數(shù)后面的帶默認初始化的參數(shù)都是可選的,與可選參數(shù)一樣,在調(diào)用函數(shù)的時候可以省略。 也就是說可選參數(shù)與末尾的默認參數(shù)共享參數(shù)類型。

function buildName(firstName: string, lastName?: string) {
    // ...
}

function buildName(firstName: string, lastName = "Smith") {
    // ...
}

共享同樣的類型(firstName: string, lastName?: string) => string。 默認參數(shù)的默認值消失了,只保留了它是一個可選參數(shù)的信息。

與普通可選參數(shù)不同的是,帶默認值的參數(shù)不需要放在必須參數(shù)的后面。 如果帶默認值的參數(shù)出現(xiàn)在必須參數(shù)前面,用戶必須明確的傳入 undefined值來獲得默認值。 例如,我們重寫最后一個例子,讓 firstName是帶默認值的參數(shù):

function buildName(firstName = "Will", lastName: string) {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams");     // okay and returns "Will Adams"

剩余參數(shù)

必要參數(shù),默認參數(shù)和可選參數(shù)有個共同點:它們表示某一個參數(shù)。 有時,你想同時操作多個參數(shù),或者你并不知道會有多少參數(shù)傳遞進來。 在JavaScript里,你可以使用 arguments來訪問所有傳入的參數(shù)。

在TypeScript里,你可以把所有參數(shù)收集到一個變量里:

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

剩余參數(shù)會被當(dāng)做個數(shù)不限的可選參數(shù)。 可以一個都沒有,同樣也可以有任意個。 編譯器創(chuàng)建參數(shù)數(shù)組,名字是你在省略號( ...)后面給定的名字,你可以在函數(shù)體內(nèi)使用這個數(shù)組。

這個省略號也會在帶有剩余參數(shù)的函數(shù)類型定義上使用到:

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;

this

學(xué)習(xí)使用JavaScript里this就好比一場成年禮。 由于TypeScript是JavaScript的超集,TypeScript程序員也需要弄清this工作機制并且當(dāng)有bug的時候能夠找出錯誤所在。 幸運的是,TypeScript能通知你錯誤地使用了 this的地方。 如果你想了解JavaScript里的 this是如何工作的,那么首先閱讀Yehuda Katz寫的Understanding JavaScript Function Invocation and "this"。 Yehuda的文章詳細的闡述了 this的內(nèi)部工作原理,因此我們這里只做簡單介紹。

this和箭頭函數(shù)

JavaScript里,this的值在函數(shù)被調(diào)用的時候才會指定。 這是個既強大又靈活的特點,但是你需要花點時間弄清楚函數(shù)調(diào)用的上下文是什么。 但眾所周知,這不是一件很簡單的事,尤其是在返回一個函數(shù)或?qū)⒑瘮?shù)當(dāng)做參數(shù)傳遞的時候。

下面看一個例子:

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        return function() {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

可以看到createCardPicker是個函數(shù),并且它又返回了一個函數(shù)。 如果我們嘗試運行這個程序,會發(fā)現(xiàn)它并沒有彈出對話框而是報錯了。 因為 createCardPicker返回的函數(shù)里的this被設(shè)置成了window而不是deck對象。 因為我們只是獨立的調(diào)用了 cardPicker()。 頂級的非方法式調(diào)用會將 this視為window。 (注意:在嚴(yán)格模式下, thisundefined而不是window)。

為了解決這個問題,我們可以在函數(shù)被返回時就綁好正確的this。 這樣的話,無論之后怎么使用它,都會引用綁定的‘deck’對象。 我們需要改變函數(shù)表達式來使用ECMAScript 6箭頭語法。 箭頭函數(shù)能保存函數(shù)創(chuàng)建時的 this值,而不是調(diào)用時的值:

我們把函數(shù)表達式變?yōu)槭褂胠ambda表達式( () => {} )。 這樣就會在函數(shù)創(chuàng)建的時候就指定了‘this’值,而不是在函數(shù)調(diào)用的時候。

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

更好事情是,TypeScript會警告你犯了一個錯誤,如果你給編譯器設(shè)置了--noImplicitThis標(biāo)記。 它會指出this.suits[pickedSuit]里的this的類型為any。

this參數(shù)

不幸的是,this.suits[pickedSuit]的類型依舊為any。 這是因為 this來自對象字面量里的函數(shù)表達式。 修改的方法是,提供一個顯式的 this參數(shù)。 this參數(shù)是個假的參數(shù),它出現(xiàn)在參數(shù)列表的最前面:

function f(this: void) {
    // make sure `this` is unusable in this standalone function
}

讓我們往例子里添加一些接口,Card 和 Deck,讓類型重用能夠變得清晰簡單些:

interface Card {
    suit: string;
    card: number;
}
interface Deck {
    suits: string[];
    cards: number[];
    createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    // NOTE: The function now explicitly specifies that its callee must be of type Deck
    createCardPicker: function(this: Deck) {
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

現(xiàn)在TypeScript知道createCardPicker期望在某個Deck對象上調(diào)用。 也就是說 thisDeck類型的,而非any,因此--noImplicitThis不會報錯了。

this參數(shù)在回調(diào)函數(shù)里

你可以也看到過在回調(diào)函數(shù)里的this報錯,當(dāng)你將一個函數(shù)傳遞到某個庫函數(shù)里稍后會被調(diào)用時。 因為當(dāng)回調(diào)被調(diào)用的時候,它們會被當(dāng)成一個普通函數(shù)調(diào)用, this將為undefined。 稍做改動,你就可以通過 this參數(shù)來避免錯誤。 首先,庫函數(shù)的作者要指定 this的類型:

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}

?this: void? 表示 ?addClickListener? 期望 ?onclick ?是一個不需要 ?this ?類型的函數(shù)。 其次,用 ?this ?注釋您的調(diào)用代碼:

class Handler {
    info: string;
    onClickBad(this: Handler, e: Event) {
        // oops, used this here. using this callback would crash at runtime
        this.info = e.message;
    };
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!

指定了this類型后,你顯式聲明onClickBad必須在Handler的實例上調(diào)用。 然后TypeScript會檢測到addClickListener要求函數(shù)帶有this: void。 改變 this類型來修復(fù)這個錯誤:

class Handler {
    info: string;
    onClickGood(this: void, e: Event) {
        // can't use this here because it's of type void!
        console.log('clicked!');
    }
}
let h = new Handler();
uiElement.addClickListener(h.onClickGood);

因為onClickGood指定了this類型為void,因此傳遞addClickListener是合法的。 當(dāng)然了,這也意味著不能使用 this.info. 如果你兩者都想要,你不得不使用箭頭函數(shù)了:

class Handler {
    info: string;
    onClickGood = (e: Event) => { this.info = e.message }
}

這是可行的因為箭頭函數(shù)不會捕獲this,所以你總是可以把它們傳給期望this: void的函數(shù)。 缺點是每個Handler對象都會創(chuàng)建一個箭頭函數(shù)。 另一方面,方法只會被創(chuàng)建一次,添加到 Handler的原型鏈上。 它們在不同 Handler對象間是共享的。

重載

JavaScript本身是個動態(tài)語言。 JavaScript里函數(shù)根據(jù)傳入不同的參數(shù)而返回不同類型的數(shù)據(jù)是很常見的。

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

pickCard方法根據(jù)傳入?yún)?shù)的不同會返回兩種不同的類型。 如果傳入的是代表紙牌的對象,函數(shù)作用是從中抓一張牌。 如果用戶想抓牌,我們告訴他抓到了什么牌。 但是這怎么在類型系統(tǒng)里表示呢。

方法是為同一個函數(shù)提供多個函數(shù)類型定義來進行函數(shù)重載。 編譯器會根據(jù)這個列表去處理函數(shù)的調(diào)用。 下面我們來重載 pickCard函數(shù)。

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

這樣改變后,重載的pickCard函數(shù)在調(diào)用的時候會進行正確的類型檢查。

為了讓編譯器能夠選擇正確的檢查類型,它與JavaScript里的處理流程相似。 它查找重載列表,嘗試使用第一個重載定義。 如果匹配的話就使用這個。 因此,在定義重載的時候,一定要把最精確的定義放在最前面。

注意,function pickCard(x): any并不是重載列表的一部分,因此這里只有兩個重載:一個是接收對象另一個接收數(shù)字。 以其它參數(shù)調(diào)用 pickCard會產(chǎn)生錯誤。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號