在JavaScript中,函数是构成任何应用程序的基础块。通过函数,你得以实现建立抽象层、模仿类、信息隐藏和模块化。在TypeScript中,虽然已经存在类和模块化,但是函数依旧在如何去"处理"事件的问题上起关键作用。TypeScript在JavaScript的标准基础上给函数添加了一些新的功能使使用者可以更好的用函数处理工作。

函数

首先,和JavaScript一样,TypeScript中的函数可以创建命名函数和匿名函数。这样你就可以为应用程序选择最合适的方式,无论是定义一系列函数API还是一次性使用的函数。

快速的回顾下JavaScript中的这两种方法是怎么样的:

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

在JavaScript中,函数可以使用函数外部的变量。当这么做的时候,我们称之为"捕获"这些变量。要理解它是怎么工作和衡量何时使用这项技术,这已经超出本文的内容范围了,透彻的理解这个机制在JavaScript和TypeScript中是多么重要的一部分是需要的。

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

函数类型

给函数添加类型

为之前的简单案例添加类型:

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

我们可以给每个参数指定类型,并且为函数本身return的值指定类型。TypeScript能够根据return语句推算出返回值的类型,所以很多情况下可以忽略它。

编写函数类型

现在我们已经为函数添加了类型,接下来为函数写出所有的类型:

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

函数类型包括两个部分:arguments(参数)的类型和return(返回)值的类型。当需要写所有的函数类型,这两部分是必需的。我们写参数类型就像写一个参数列表一样,每个参数给定一个名称和类型。这个名称只是为了增加可读性,我们可以这样写:

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

只要参数类型正确,它就被认为是有效的函数类型,而不用去在乎参数名称是否正确。

第二部分是返回值类型。我们在参数和返回值类型之间用肥肥的箭头(=>)来明确这个类型。正如前面所说,这只是函数类型的一部分,所以当不存在返回值时,应该使用"void",而不是任由它空着。

注:只有参数和返回值的类型组成了函数类型。捕获到的变量不会在类型中体现。实际上,捕获的变量属于函数"隐藏状态"部分的,并且也不是API的组成部分。

类型推断

在例子中,你可能已经注意到,当你在赋值语句的任意一边指定类型,TypeScript编译器都能够在另一边自动识别类型:

// myAdd 函数中所有的类型
var myAdd = function(x: number, y: number): number { return x+y; }; // 参数'x'和'y'是number类型
var myAdd: (baseValue:number, increment:number)=>number =
function(x, y) { return x+y; };

这称为"上下文(语境)归类",一种类型推断的形式。这有助于减少工作量并且保持程序的类型。

可选参数和默认参数

和JavaScript不同,TypeScript函数中的每个参数都是必需的。这并不意味着不可以传入"null"值,当一个函数被调用的时候,编译器会检查用户是否为每一个参数提供值。编译器也会假设这些参数就是需要被传入函数的参数。简而言之,函数的传入参数的个数必须和函数所期望被传入参数的个数相等。

function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
} var result1 = buildName("Bob"); // 错误,传的参数太少
var result2 = buildName("Bob", "Adams", "Sr."); // 错误,传的参数太多
var result3 = buildName("Bob", "Adams"); // 额,这是正确的

在JavaScript中,每一个参数都是可选的,用户可以在恰当的时候不用传某个参数。这样做就相当于传入"undefined"代替这个参数。在TypeScript中,我们可以在参数后面加上"?"符号,让这个参数变成可选参数。例如,我们想要"lastName"是可选的:

function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
} var result1 = buildName("Bob"); // 正常运行
var result2 = buildName("Bob", "Adams", "Sr."); // 错误,传的参数太多
var result3 = buildName("Bob", "Adams"); // 额,这是正确的

可选参数必须放在必需参数后面(存在必需参数的情况下)。假如我们要"firstName"变成可选参数而不是"lastName",我们需要改变函数参数的排序,将"firstName"放到后面。

在TypeScript中也可以为某个参数设置值,当用户未提供该参数时,将使用这个值。这被称为"默认值"。将上个例子中的"lastName"的默认值设置为"Smith":

function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
} var result1 = buildName("Bob"); // 正常运行
var result2 = buildName("Bob", "Adams", "Sr."); // 错误,传的参数太多
var result3 = buildName("Bob", "Adams"); // 额,这是正确的

和可选参数一样,默认参数必须放在必需参数后面(存在必需参数的情况下)。

可选参数和默认参数共享类型。如

function buildName(firstName: string, lastName?: string) {

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

共享同一个类型 "(firstName: string, lastName?: string)=>string"。

其他参数

必需参数,可选参数和默认参数都有以个共同点:它们只表示一个参数。有些时候可能想要多个参数,或者不知道具体有多少个参数最终需要被传入。在JavaScript中,你可以使用arguments来访问函数传入的所有参数。

在TypeScript中,你可以将所有的参数聚集到一个变量中:

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

其他参数被视为数量无限的可选参数。你可以一个都不传,也可以传任意你想传的个数。编译器将会生成一个你传入函数的参数数组,以省略号("...")之后的名称命名,你可以在函数中使用这个数组。

省略号也用在带有其他参数的函数类型:

function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
} var buildNameFun: (fname: string, ...rest: string[])=>string = buildName;

Lambdas和使用"this"

对于JavaScript程序员来说,关于"this"工作机制的话题算是老生常谈了。确实,学会如何使用"this"是JavaScript程序员成长中必须经历的。因为TypeScript是JavaScript的一个超集,TypeScript程序员也需要学会如何让使用"this"并且如何处理错误使用"this"引发的问题。假如要用来描述如何使用JavaScript中的"this",一整篇文章都可以写,并且该类文章也有很多。在这里只介绍基础知识。

在JavaScript中,"this"在函数被调用的时候被指定。这使得它强大而又灵活,只是你需要为理解函数执行的上下文付出代价。众所周知,这是很麻烦的,比如,当一个函数当作回调函数使用的时候。

来看一个例子:

var deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return function() {
var pickedCard = Math.floor(Math.random() * 52);
var pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
} var cardPicker = deck.createCardPicker();
var pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit);

试着运行这个例子,我们会得到一个错误,而不是预期的弹出警告框。因为'createCardPicker'返回的函数里"this"指向的是Window对象而不是"deck"对象。所以在这里调用"cardPicker()",将Window对象绑定到了"this"上。(注意:严格模式下,this是undefined而不是Window)

我们可以在返回函数的时候就绑定正确的"this",这样,无论后面怎么调用这个函数,"this"依旧会指向"deck"对象。
为了解决这问题,我们可以使用lambda语法代替JavaScript函数表达式。它将会在函数创建的时候自动捕获"this"变量,而不是在被调用的时候处理"this"。

var deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
// 注意: 下面这行现在是使用lambda语法, 更早的捕获'this'
return () => {
var pickedCard = Math.floor(Math.random() * 52);
var pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
} var cardPicker = deck.createCardPicker();
var pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit);

想要了解关于"this"的更多信息,可以阅读Yahuda Katz的 理解JavaScript函数调用和"this"

重载
JavaScript本身就是动态语言。根据传入参数的不同而返回不同类型的值在JavaScript函数中用的并不少。

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

function pickCard(x) {
// 检查我们处理的是什么类型的参数
// 如果是数组对象, 则给定deck并且选择card
if (typeof x == "object") {
var pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// 否则只选择card
else if (typeof x == "number") {
var pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
} var myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
var pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); var pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

这里的"pickCard"将会根据用户传入的参数不同而返回不同的数据。如用户传入的是代表"deck"的对象,则函数将会选择一个"card"并且返回。如果用户已经选择了"card",我们则会返回他选择的是哪个"card"。但是怎么在类型系统里描述这些呢。

解决方案是基于同一个函数提供多个函数类型,就如函数重载列表。这个列表将被编译器用来解决函数的调用。现在来创建一个重载列表,描述"pickCard"接收的是什么,返回的是什么。

var 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 {
// 检查我们处理的是什么类型的参数
// 如果是数组对象, 则给定deck并且选择card
if (typeof x == "object") {
var pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// 否则只选择card
else if (typeof x == "number") {
var pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
} var myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
var pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); var pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

有了这些改变,重载后的"pickCard"函数会在被调用的时候进行合适的类型检查。

为了编译器选择正确的类型检查,它和JavaScript底层运行是相似的。它会查找重载列表,使用第一次定义的函数进行检查,如果能匹配,则调用当前这个函数。正是因为如此,一般都会将最明确的定义放在前面。

注意,"function pickCard(x: any): any"不是重载列表的一部分。所以它只有2次重载:一个针对的是object,一次针对的是number。调用"pickCard"并且传入其他类型的参数将会导致错误。

TypeScript Function(函数)的更多相关文章

  1. 从C#到TypeScript - function

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  2. TypeScript 中函数的理解?与 JavaScript 函数的区别?

    一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...

  3. 关于Function()函数对象的那些小九九

    概念:首先,函数是一种特殊类型的数据,函数也是数据类型的一种,实际上函数也是一种对象,函数对象的内建构造器是Function(); 函数的几种创建方式: 函数声明法: function sum(a,b ...

  4. JavaScript function函数种类(转)

    转自:http://www.cnblogs.com/polk6/p/3284839.html JavaScript function函数种类 本篇主要介绍普通函数.匿名函数.闭包函数 目录 1. 普通 ...

  5. JavaScript function函数种类介绍

    JavaScript function函数种类介绍 本篇主要介绍普通函数.匿名函数.闭包函数 1.普通函数介绍 1.1 示例 ? 1 2 3 function ShowName(name) {     ...

  6. 【JS学习笔记】关于function函数

    函数的基本格式 function 函数名() { 代码: } 函数的定义和调用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...

  7. 2019-2-14SQLserver中function函数和存储过程、触发器、CURSOR

    Sqlserver 自定义函数 Function使用介绍 前言:         在SQL server中不仅可以可以使用系统自带的函数(时间函数.聚合函数.字符串函数等等),还可以根据需要自定义函数 ...

  8. 创建一个Scalar-valued Function函数来实现LastIndexOf

    昨天有帮助网友解决的个字符串截取的问题,<截取字符串中最后一个中文词语(MS SQL)>http://www.cnblogs.com/insus/p/7883606.html 虽然实现了, ...

  9. javascript:function 函数声明和函数表达式 详解

    函数声明(缩写为FD)是这样一种函数: 有一个特定的名称 在源码中的位置:要么处于程序级(Program level),要么处于其它函数的主体(FunctionBody)中 在进入上下文阶段创建 影响 ...

随机推荐

  1. SQL Server 中WITH (NOLOCK)浅析

    概念介绍 开发人员喜欢在SQL脚本中使用WITH(NOLOCK), WITH(NOLOCK)其实是表提示(table_hint)中的一种.它等同于 READUNCOMMITTED . 具体的功能作用如 ...

  2. 关于xml的使用。

    使用的常用类: XmlSerializer ParaMapping StreamReader DirectionaryInfo FileInfo using as object 例子: public ...

  3. ActiveMQ 5 入门

    apache-activemq-5.13.2 ActiveMQ使用JAAS授权的配置方式 <plugins> <!– 使用login.config配置授权用户 --> < ...

  4. 开源一个windows下的定时任务框架,简单粗暴好用。

    这里是你想要的功能: 支持插件,将你要执行的任务编译成程序集放到框架的根目录下,再进行简单的配置就行了. 支持Corn表达式.想让任务在什么时候执行就在什么时候执行. 支持安装成windows ser ...

  5. python 数据处理学习pandas之DataFrame

    请原谅没有一次写完,本文是自己学习过程中的记录,完善pandas的学习知识,对于现有网上资料的缺少和利用python进行数据分析这本书部分知识的过时,只好以记录的形势来写这篇文章.最如果后续工作定下来 ...

  6. WPF 3D 知识点大全以及实例

    引言 现在物联网概念这么火,如果监控的信息能够实时在手机的客服端中以3D形式展示给我们,那种体验大家可以发挥自己的想象. 那生活中我们还有很多地方用到这些,如上图所示的Kinect 在医疗上的应用,当 ...

  7. 字符串切分 String.Split 和 Regex.Split

    当切割字符串的是单个字符时可使用String.Split string strSample="ProductID:20150215,Categroy:Food,Price:15.00&quo ...

  8. POJ3281Dining[最大流]

    Dining Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16352   Accepted: 7307 Descripti ...

  9. java操作Redis

    需要使用如下jar包 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> ...

  10. Map接口使用注意事项

    1,Map接口对象本身不能直接使用迭代进行输出的.因为map每个位置存放的是一对值. 而iterator每次只能找到一个值.如果一定要迭代输出,可以通过以下步骤.: 但是,Map接口只作为查找使用,输 ...