TypeScript - 函数详解

JS中创建函数的两种方式

// 命名函数
function add(x, y) {
  return x + y;
}

// 匿名函数
let myAdd = function(x, y) { return x + y; };

函数类型

定义函数类型

参数类型 x: number
返回值类型 function add(x: number, y: number): number TypeScript可以通过查看return语句来弄清楚返回类型。通常省略不写。但是如果没有返回值推荐写上void而不是留空

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

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

function checkIn (x:string): void{
    console.log('haha');
}

完整函数类型

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

let newAdd: (baseValue: number, increment: number) => number =
function(x: number, y: number): number { return x + y; }; //在TypeScript里,只要参数类型匹配。就是有效的函数类型。不在乎你参数名是否正确

myAdd(1, 2) //3
newAdd(1, 2) //3

推断类型

即使你在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型。这叫做”上下文归类”

// myAdd 有完整的函数类型
let myAdd = function(x: number, y: number): number { return  x + y; };

// x, y有number类型
let newAdd: (baseValue: number, increment: number) => number =
    function(x, y) { return x + y; };

可选参数,默认参数

在TypeScript中, 每个函数参数都是必须的,编译器会检查用户是否为每个参数都传入了值(null和undefined也算)。传入的参数个数必须跟函数的参数个数保持一致。

区别: 在Javascript中,每个参数都是可传可不传的。没传值的时候,参数的值就是undefined

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

let result1 = buildName("Bob");                  // erro 少入要求的参数个数
let result2 = buildName("Bob", "Adams", "Sr.");  // error 超处要求的参数个数
let result3 = buildName("Bob", "Adams"); // "Bob Adams"

可选参数
写法:参数名?:类型
注:可选参数必须跟在必须参数后面。

function buildName(firstName: string, lastName?: string) {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
  }
  
  let result1 = buildName("Bob"); // "Bob"
  let result2 = buildName("Bob", "Adams", "Sr.");  // error 超处要求的参数个数
  let result3 = buildName("Bob", "Adams");  // "Bob Adams" 

默认参数

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

let result1 = buildName("Bob");                  // "Bob Smith"
let result2 = buildName("Bob", undefined);       // "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr.");  // error 超处要求的参数个数
let result4 = buildName("Bob", "Adams");         // "Bob Adams"

实际上在必须参数后定义默认参数跟定义可选参数是共享同样的类型

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

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

都是共享的同样类型

(firstName: string, lastName?: string) => string

默认参数可以任意放置位置,但是注意如果默认值的参数出现在必须参数前面,就必须要明确的传入 undefined才能获取默认值。

function buildName(firstName= 'Bob', lastName: string) {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
  }
  
  let result1 = buildName("Bob"); // error 少入要求的参数个数
  let result2 = buildName("Bob", "Adams", "Sr.");  // error 超处要求的参数个数
  let result3 = buildName(undefined, "Adams");  // "Bob Adams"

剩余参数

利用扩展运算符,在不确定有多少个参数的时候使用

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

buildName("Joseph", "Samuel", "Lucas", "MacKinzie"); //"Joseph Samuel Lucas MacKinzie"
function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

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

buildNameFun("Joseph", "Samuel", "Lucas", "MacKinzie") // "Joseph Samuel Lucas MacKinzie"

this

如果你想了解JavaScript里的this是如何工作的,那么首先阅读Yehuda Katz写的Understanding JavaScript Function Invocation and “this”

在JavaScript,this是一个当函数被调用时设置的变量。This让函数变成一个强大且变通的功能。但带来的成本是你得弄清楚当一个函数被执行时的上下文。众所周知,这很令人苦恼,尤其是在返回函数或将函数作为参数传递时。(P.s非常赞同这段话,一起来复习一下这甜蜜的烦恼吧)

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 }; //Cannot read property 'suits' of undefined
//调用时候: 非严格模式, this其实是window. 严格模式时, this是undefined
        };
    }
};

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

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

解决方案
使用箭头函数绑定this

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);

this参数

在上述例子中, this.suits[pickedSuit]的类型是any. 因为this来自于函数. 要解决这个,需要提供一个明确的this 参数。 this是伪参数来自于函数参数列表的第一个

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);

回调里面的this参数

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void; //this: void 意味着addClickListener希望传进来的onclick是一个方法,并且没有调用this
}

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();
let uiElement: UIElement;
uiElement.addClickListener(h.onClickBad); //Error!!! 传进去的this是Handler类型。 addClickListener要求this是void

修复上面的error,需要把this: Handler 改成 this:void. 并且方法体内部也不能调用this.

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void; //this: void 意味着addClickListener希望传进来的onclick是一个方法,并且没有调用this
}

class Handler {
    info: string;
    onClickBad(this: void, e: Event) {
        // this.info = e.message;
        console.log('clicked');
    }
}
let h = new Handler();
let uiElement: UIElement;
uiElement.addClickListener(h.onClickBad);

如果还是想用this,可以使用箭头函数。

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void; //this: void 意味着addClickListener希望传进来的onclick是一个方法,并且没有调用this
}

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

let h = new Handler();
let uiElement: UIElement;
uiElement.addClickListener(h.onClickBad);

重载(Overload)

JavaScript是一个非常动态的语言,在JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。

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

function pickCard(x: any): 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); // card: 4 of hearts

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

如果想为重载做类型检查,如何做呢?

需要从最具体到最不具体的类型检查对重载进行排序,编译器遵循与基础javaScript 相似的过程,它会查看重载列表,并在第一个重载之前尝试使用提供的参数调用该函数。如果找到匹配项,它将会选择此重载作为正确的重载。

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): 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(x: any) 任何块不属于重载列表。只有1和2是重载。一个接收 对象,一个接收数字

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };

你可能感兴趣的:(TypeScript,javascript,js,typescript,前端)