TypeScript Function(函数)
在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(函数)的更多相关文章
- 从C#到TypeScript - function
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- TypeScript 中函数的理解?与 JavaScript 函数的区别?
一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...
- 关于Function()函数对象的那些小九九
概念:首先,函数是一种特殊类型的数据,函数也是数据类型的一种,实际上函数也是一种对象,函数对象的内建构造器是Function(); 函数的几种创建方式: 函数声明法: function sum(a,b ...
- JavaScript function函数种类(转)
转自:http://www.cnblogs.com/polk6/p/3284839.html JavaScript function函数种类 本篇主要介绍普通函数.匿名函数.闭包函数 目录 1. 普通 ...
- JavaScript function函数种类介绍
JavaScript function函数种类介绍 本篇主要介绍普通函数.匿名函数.闭包函数 1.普通函数介绍 1.1 示例 ? 1 2 3 function ShowName(name) { ...
- 【JS学习笔记】关于function函数
函数的基本格式 function 函数名() { 代码: } 函数的定义和调用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...
- 2019-2-14SQLserver中function函数和存储过程、触发器、CURSOR
Sqlserver 自定义函数 Function使用介绍 前言: 在SQL server中不仅可以可以使用系统自带的函数(时间函数.聚合函数.字符串函数等等),还可以根据需要自定义函数 ...
- 创建一个Scalar-valued Function函数来实现LastIndexOf
昨天有帮助网友解决的个字符串截取的问题,<截取字符串中最后一个中文词语(MS SQL)>http://www.cnblogs.com/insus/p/7883606.html 虽然实现了, ...
- javascript:function 函数声明和函数表达式 详解
函数声明(缩写为FD)是这样一种函数: 有一个特定的名称 在源码中的位置:要么处于程序级(Program level),要么处于其它函数的主体(FunctionBody)中 在进入上下文阶段创建 影响 ...
随机推荐
- MySQL 使用XtraBackup的shell脚本介绍
mysql_backup.sh是关于MySQL的一个使用XtraBackup做备份的shell脚本,实现了简单的完整备份和增量备份.以及邮件发送备份信息等功能.功能目前还比较简单,后续将继续完善和增加 ...
- Linux下安装Nginx1.9.3
操作系统:CentOS6.5 64bit Nginx: 1.9.3 1.下载Nginx [root@iZ94jj63a3sZ softs]# wget http://nginx.org/downloa ...
- 【WPF系列】基础学习-WPF设计模式概览
引言 “设计模式”这个让程序员提起来就“酷”的东东,着实让让初学设计模式的programer自我陶醉一番.太多的经历,告诉我们“凡事都要个度,要学会适可而止”,否则过犹不及.“设计模式”也一样,切莫为 ...
- OpenStack 企业私有云的若干需求(10):OpenStack 的前景和钱景
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- MMORPG大型游戏设计与开发(服务器 游戏场景 搜索机)
双十一注定是忙碌的日子,所以到了现在我才将今天自己学习的内容拿出来跟大家分享.搜索机是我自己暂时取的名字,其实简单的说就是场景里提供搜索的一个工具,负责场景对象的范围搜索和获取.空洞的理论总是让人一头 ...
- 【2016-11-10】【坚持学习】【Day23】【Socket 编程初步了解】
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.
- httpclient进行basic auth认证
private HttpClientContext context = HttpClientContext.create(); public void addUserOAuth(String user ...
- BZOJ 1070: [SCOI2007]修车 [最小费用最大流]
1070: [SCOI2007]修车 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 4936 Solved: 2032[Submit][Status] ...
- 洛谷P1196 银河英雄传说[带权并查集]
题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...
- CF724D. Dense Subsequence[贪心 字典序!]
D. Dense Subsequence time limit per test 2 seconds memory limit per test 256 megabytes input standar ...