TypeScript 笔记(二、函数)

函数是JavaScript应用程序的基础,用于定义行为。TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。

本文将从如下几个方面,介绍TypeScript中的函数。

  • 函数定义
  • 函数的参数
  • 函数重载
  • this关键字

1. 函数定义

函数是JavaScript中的一等公民。
可以像对待任何其他数据类型一样对待它们——把它们存在数组里,当作参数传递,赋值给变量…等等。

函数定义方式可以分为两大类:函数声明(有名字的函数)、函数表达式(匿名函数)。如下列举了已知的函数定义方式:

//1. 函数声明
function add(x: number, y: number): number { return x + y; }

//2. 函数表达式(完整写法)
let myAdd1: (x:number, y:number) => number = function(x: number, y: number): number { return x + y; };

//3. 函数表达式(简写)
let myAdd2 = function(x: number, y: number): number { return x + y; };

//4. 函数表达式(ES6中的箭头函数)
let myAdd3 = (x:number, y:number) => x + y;
let myAdd4 = (x:number, y:number) => { return x + y; };
let myAdd5 = (x:number, y:number):number => { return x + y; };
let myAdd6: (x:number, y:number) => number = (x:number, y:number):number => { return x + y; };

下面对上述代码进行说明:

  1. 函数声明:这种方式固定了函数名称,Typescript使用冒号指定了参数类型和返回值类型,很容易理解。
  2. 函数表达式(完整写法):等号左边的(x:number, y:number) => number用来描述函数的类型,类似myAdd1:string代表定义了一个字符串。等号右边则是一个普通的匿名函数。
  3. 函数表达式(简写):这种写法省略了(x:number, y:number) => number,省略的部分会被Typescript自动推断出来。
  4. 函数表达式(ES6中的箭头函数):利用ES6中新增的箭头函数,可以省略匿名函数的function关键字,当只有一行处理代码时,可以再省略大括号。

注意

在上述函数表达式(完整写法)部分的(x:number, y:number) => number代码中,=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。可以这样理解:描述字符串使用:let myAdd2:string,描述函数时把string替换为上述代码。
而在函数表达式(ES6中的箭头函数)部分的(x:number, y:number):number => { return x + y; };代码中,则是ES6规范的实现,=>叫做箭头函数。

2. 函数的参数

JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 而TypeScript里的每个函数参数都是必须的,传递给一个函数的参数个数必须与函数期望的参数个数一致。

为了保持TypeScript的灵活性,TypeScript也提供了相对灵活参数传递功能,一共有如下三种:

  1. 可选参数:如lastName?: string
  2. 指定参数默认值(ES6规范):如lastName = "Smith"
  3. 剩余参数(ES6规范):参数个数不确定,如...restOfName: string[]
//lastName为可选参数,可选参数必须跟在必须参数后面。
function buildName1(firstName: string, lastName?: string) {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
}

//为lastName指定默认值, 如果lastName参数在firstName之前,则需要传入undefined来获得默认值
function buildName2(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}

//指定剩余参数
function buildName3(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName3("Joseph", "Samuel", "Lucas", "MacKinzie");

3. 函数重载

JavaScript中是不允许方法重载的,原因是即使参数不同,也会被认为是同一个方法进而提示方法重复。

为此,Typescript提供了语法糖式的解决方案,允许声明同名、但参数类型不同的函数,但不能提供具体实现;在最终的实现代码中继续用原始的方式对参数进行判断,从而实现了方法重载。

此时,只允许调用没有具体实现的重载函数,不允许调用重载的真正实现的函数。

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("ss");//提示错误信息,不允许直接调用 pickCard(x): any

4. this关键字

JavaScript中,this的值在函数被调用的时候才会指定。这是个既强大又灵活的特点,当返回一个函数或函数作为参数传递时,弄清楚上下文不是一件容易的事情。

为此,ES6的箭头函数新增了保存函数创建时的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);

5. 小结

本文介绍了TypeScript中的函数,相比JavaScript中的函数,TypeScript为函数增加了更多的约束:参数类型和返回值类型的静态化,不匹配时不允许调用。静态化参数类型后,使得函数的重载成为可能。

另一方面,对ES6中箭头函数的实现,使得this关键字代表的意义可以在函数调用时函数创建时自由切换。

参考的文章

TypeScript 中文手册-函数(function)
TypeScript 函数 | 菜鸟教程
函数的类型 - TypeScript 入门教程
ECMAScript 6入门


文章作者: 沉迷思考的鱼
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 沉迷思考的鱼 !
评论
 上一篇
TypeScript 笔记(三、类和接口) TypeScript 笔记(三、类和接口)
作为一个面向对象的语言,类和接口不可或缺,相比现行的JavaScript,这两个概念都是新引入的,本文将对这两个概念进行详细介绍。 1. 什么是类传统方法中,JavaScript通过构造函数实现类的概念,通过原型链实现继承。而在ES6中,
2019-06-03
下一篇 
TypeScript 笔记(一、简介) TypeScript 笔记(一、简介)
由于近期时常会写一些前端页面和相关的js,每次写复杂js的时候都是一阵烦躁,对于没有语法检查、动态类型、难封装这些特点深恶痛绝。跟前端的同事了解了下他们用的技术栈,得知了一个前端利器:TypeScript。 本文将从如下几个方面,介绍Typ
2019-05-17
  目录