函数是JavaScript中任意应用程序的基本构件块。能够在函数基础上创建抽象层,模拟类,信息隐藏,模块。在TypeScript中,虽然已经有类和模块,但函数函数仍然扮演着如何作事的关键角色。TypeScript还在标准JavaScript 函数基础上增长了一些新的能力,来使得函数更容易使用。
javascript
TypeScript与JavaScript同样,均可以建立命名函数,也能够建立匿名函数。这样容许开发人员根据应用程序选择最合适的方式,不管是在一个API中创建一列函数仍是创建一个one-off函数来转向另外一个函数。
先快速看下JavaScript中这两种方法是什么样的:
java
// Named function-命名函数 function add(x, y) { return x+y; } //Anonymous function-匿名函数 var myAdd = function(x, y) { return x+y; };
就像JavaScript中同样,函数能够返回变量。当返回变量时就称‘捕获’这些变量。理解这是如何工做,以及使用该技术时须要注意哪些事项,虽然超出了本片文章范围,但要掌握JavaScript和TypeScript语言,就须要对该机制可以有透彻理解。
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; };
咱们对每一个参数添加类型,而后对函数返回值添加类型。TypeScript能够经过查看返回语句算出返回类型,因此在许多状况下也能够选择不添加返回类型。
数组
如今咱们已经对函数添加了类型,如今咱们写出函数的完整类型,来看看函数类型的每一部分。
dom
var myAdd: (x:number, y:number)=>number = function(x: number, y: number): number { return x+y; };
一个函数的类型有相同的两部分:参数类型和返回类型。当写下全部函数类型时,这两部分都须要写出。参数类型就像一个参数列表同样,每一个参数有一个名称和一个类型。名称只是有助于可读性。也能够写为:
函数
var myAdd: (baseValue:number, increment:number)=>number = function(x: number, y: number): number { return x+y; };
只要函数的参数类型一一对应,就认为是有效类型,而不用考虑参数名称是否相同。
第二部分是返回类型。咱们在参数和返回类型之间使用fat arrow (=>)使返回类型更为清晰。就像前面提到的,这是函数类型所需的一部分,因此函数若是没有返回值,应当用'void'而不是什么也不填写。
注意,只有参数类型和返回类型组成了函数类型。捕获的变量并不反映在类型中。实际上,捕获变量是函数‘隐藏状态’的一部分,不是API的一部分。
学习
下面例子中,你会注意到若是在等式的一边有类型而另外一边没有类型时,TypeScript编译器能够算出类型:
ui
// myAdd has the full function type // myAdd有完整的函数类型 var myAdd = function(x: number, y: number): number { return x+y; }; // The parameters 'x' and 'y' have the type number // 参数'x' 与 'y'的类型是number var myAdd: (baseValue:number, increment:number)=>number = function(x, y) { return x+y; };
这被称为'contextual typing'(上下文类型推断),一种类型推断形式。这有助于减小程序中须要键入的类型。
this
与JavaScript不一样,在TypeScript中认为函数的每一个参数都是必须的。这并不表示参数取值不是为'null',而是当编译器调用函数时将检查每一个参数的值。编译器还假定只有这些参数传递给函数。简言之,输入的函数参数数量必须与函数期待的参数数量相同。
function buildName(firstName: string, lastName: string) { return firstName + " " + lastName; } var result1 = buildName("Bob"); //error, too few parameters var result2 = buildName("Bob", "Adams", "Sr."); //error, too many parameters var result3 = buildName("Bob", "Adams"); //ah, just right
在JavaScript中,每一个参数都被视为可选参数,用户能够不填充参数,此时这些未填充的参数自动取值为undefined。在TypeScript中能够在可选参数旁边用'?'来实现相同功能。例如但愿last name为可选参数:
function buildName(firstName: string, lastName?: string) { if (lastName) return firstName + " " + lastName; else return firstName; } var result1 = buildName("Bob"); //works correctly now var result2 = buildName("Bob", "Adams", "Sr."); //error, too many parameters var result3 = buildName("Bob", "Adams"); //ah, just right
可选参数必须跟在必选参数后面。假定想要使first name而不是last name为可选参数,就须要改变函数参数顺序,将first name参数放在后面。
在TypeScript中,当用户没有对可选参数提供值时能够事先设置一个值,这些参数也被称为缺省参数。之前面例子举例,设置last name的缺省值为"Smith"。
function buildName(firstName: string, lastName = "Smith") { return firstName + " " + lastName; } var result1 = buildName("Bob"); //works correctly now, also var result2 = buildName("Bob", "Adams", "Sr."); //error, too many parameters var result3 = buildName("Bob", "Adams"); //ah, just right
就像可选参数同样,在参数列表中缺省参数也必须放在必选参数后面。
可选参数和缺省参数都拥有相同的类型:
function buildName(firstName: string, lastName?: string) {
与
function buildName(firstName: string, lastName = "Smith") {
有相同的类型 "(firstName: string, lastName?: string)=>string",缺省参数对应的缺省值消失了,只剩下可选参数。
必选参数,可选参数,缺省参数都有同样相同:一次只涉及一个参数。有时候但愿将多个参数归为一组,或不清楚函数最终会传入多少个参数。在JavaScript中,能够用函数体内可见的可变参数来表示。
在TypeScript中,能够将这些参数组合成一个变量:
function buildName(firstName: string, ...restOfName: string[]) { return firstName + " " + restOfName.join(" "); } var employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
Rest参数被看作无穷数量个可选参数。用户能够不输入参数,或根据实际状况输入N个参数。编译器将函数中在省略号...后面的参数名称用于构建一个参数数组,这样能够在函数中使用。
省略号...也用在函数rest参数的类型中:
function buildName(firstName: string, ...restOfName: string[]) { return firstName + " " + restOfName.join(" "); } var buildNameFun: (fname: string, ...rest: string[])=>string = buildName;
在JavaScript函数中'this'如何工做是学习JavaScript编程人员常见的问题。事实上,学习如何使用它就像是开发人员对JavaScript愈来愈驾轻就熟的一种成长仪式。因为TypeScript是JavaScript的一个超集,TypeScript开发人员也须要学习如何使用'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);
若是运行这个例子,会获得一个错误而不是预期的alert box。这是由于函数中用到的'this'是由'createCardPicker'建立的,它被设置为'window'而不是'deck'对象。当调用'cardPicker()'时就会发生,这里'this'除了Window之外没有动态绑定。(备注:在严格模式下,this将等于undefined而不是window)。
能够在函数返回前将函数绑定到正确的'this'变量来修复该问题。这样不用考虑函数在后面如何使用,就可以看到最初的'deck' 对象。
为了修复问题,咱们用lambda语法( ()=>{} )而非JavaScript函数表达式来表示函数。这样当函数建立时就自动捕获'this'而不是在函数被调用时捕获:
var deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { // Notice: the line below is now a lambda, allowing us to capture 'this' earlier 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的Understanding JavaScript Function Invocation and “this”。
译者注:这篇参考文章的核心思想:
fn(...args)等同于fn.call(window [ES5-strict: undefined], ...args)
(function() {})()等同于(function() {}).call(window [ES5-strict: undefined)
JavaScript本质上就是一种动态语言。常常能够看到一个JavaScript函数能够基于传递参数的形(shape)返回不一样类型的对象。
var suits = ["hearts", "spades", "clubs", "diamonds"]; 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") { var pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the 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的对象(一副牌),函数就pick the card(从中挑选一张牌);若是用户选择一个数字,就告诉用户选择的是什么牌。但类型系统中如何来描述呢?
答案是对同一个函数提供多个函数类型来重载(overloads)。编译器用这个列表来解析函数调用。下面建立一组重载函数,来描述'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 { // 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") { var pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the 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'代码片断不是重载列表,这里只有两个重载函数:一个函数接受对象,一个函数接受一个数字。调用'pickCard'时传入其余类型参数会致使错误。
[1] http://www.typescriptlang.org/Handbook#functions
[2] TypeScript系列1-简介及版本新特性, http://my.oschina.net/1pei/blog/493012
[3] TypeScript手册翻译系列1-基础类型, http://my.oschina.net/1pei/blog/493181
[4] TypeScript手册翻译系列2-接口, http://my.oschina.net/1pei/blog/493388
[5] TypeScript手册翻译系列3-类, http://my.oschina.net/1pei/blog/493539
[6] TypeScript手册翻译系列4-模块, http://my.oschina.net/1pei/blog/495948