在JavaScript中,函数是构成任何应用程序的基础块。经过函数,你得以实现创建抽象层、模仿类、信息隐藏和模块化。在TypeScript中,虽然已经存在类和模块化,可是函数依旧在如何去"处理"事件的问题上起关键做用。TypeScript在JavaScript的标准基础上给函数添加了一些新的功能使使用者能够更好的用函数处理工做。javascript
函数java
首先,和JavaScript同样,TypeScript中的函数能够建立命名函数和匿名函数。这样你就能够为应用程序选择最合适的方式,不管是定义一系列函数API仍是一次性使用的函数。程序员
快速的回顾下JavaScript中的这两种方法是怎么样的:数组
// 命名函数 function add(x, y) { return x+y; } // 匿名函数 var myAdd = function(x, y) { return x+y; };
在JavaScript中,函数可使用函数外部的变量。当这么作的时候,咱们称之为"捕获"这些变量。要理解它是怎么工做和衡量什么时候使用这项技术,这已经超出本文的内容范围了,透彻的理解这个机制在JavaScript和TypeScript中是多么重要的一部分是须要的。dom
var z = 100; function addToZ(x, y) { return x+y+z; }
函数类型模块化
给函数添加类型函数
为以前的简单案例添加类型:ui
function add(x: number, y: number): number { return x+y; } var myAdd = function(x: number, y: number): number { return x+y; };
咱们能够给每一个参数指定类型,而且为函数自己return的值指定类型。TypeScript可以根据return语句推算出返回值的类型,因此不少状况下能够忽略它。this
编写函数类型spa
如今咱们已经为函数添加了类型,接下来为函数写出全部的类型:
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"而且传入其余类型的参数将会致使错误。