版权

文章转载自:https://github.com/zhongsp

建议您直接跳转到上面的网址查看最新版本。

介绍

函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义行为的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易的使用。

函数

和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式,不论是定义一系列API函数还是只使用一次的函数。

通过下面的例子可以迅速回想起这两种JavaScript中的函数:

// Named function
function add(x, y) {
return x + y;
} // Anonymous function
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; };

我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常省略它。

书写完整函数类型

现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。

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; };

只要参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确。

第二部分是返回值类型。 对于返回值,我们在函数和返回值类型之前使用(=>)符号,使之清晰明了。 如之前提到的,返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为void而不能留空。

函数的类型只是由参数类型和返回值组成的。 函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成API的一部分。

推断类型

尝试这个例子的时候,你会发现如果你在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型:

// myAdd has the full function type
var myAdd = function(x: number, y: number): number { return x + y; }; // The parameters `x` and `y` have the type number
var myAdd: (baseValue:number, increment:number) => number =
function(x, y) { return x + y; };

这叫做“按上下文归类”,是类型推论的一种。 它帮助我们更好地为程序指定类型。

可选参数和默认参数

TypeScript里的每个函数参数都是必须的。 这不是指不能传递nullundefined作为参数,而是说编译器检查用户是否为每个参数都传入了值。 编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。

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是可选的,那么就必须调整它们的位置,把first name放在后面。

在TypeScript里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是undefined时。 它们叫做有默认初始化值的参数。 让我们修改上例,把last name的默认值设置为"Smith"

function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
} var result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
var result2 = buildName("Bob, undefined"); // still works, also returns "Bob Smith"
var result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
var result4 = buildName("Bob", "Adams"); // ah, just right

在所有必须参数后面的带默认初始化的参数都是可选的,与可选参数一样,在调用函数的时候可以省略。 也就是说可选参数与末尾的默认参数共享参数类型。

function buildName(firstName: string, lastName?: string) {
// ...
}

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

共享同样的类型(firstName: string, lastName?: string) => string。 默认参数的默认值消失了,只保留了它是一个可选参数的信息。

与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入undefined值来获得默认值。 例如,我们重写最后一个例子,让firstName是带默认值的参数:

function buildName(firstName = "Will", 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"); // okay and returns "Bob Adams"
var result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"

剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在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;

Lambda表达式和使用this

JavaScript里this的工作机制对JavaScript程序员来说已经是老生常谈了。 的确,学会如何使用它绝对是JavaScript编程中的一件大事。 由于TypeScript是JavaScript的超集,TypeScript程序员也需要弄清this工作机制并且当有bug的时候能够找出错误所在。 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()时会发生这种情况。这里没有对this进行动态绑定因此为window。(注意在严格模式下,会是undefined而不是window)。

为了解决这个问题,我们可以在函数被返回时就绑好正确的this。 这样的话,无论之后怎么使用它,都会引用绑定的‘deck’对象。

我们把函数表达式变为使用lambda表达式( () => {} )。 这样就会在函数创建的时候就指定了‘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"

重载

JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。

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方法根据传入参数的不同会返回两种不同的类型。 如果传入的是代表纸牌的对象,函数作用是从中抓一张牌。 如果用户想抓牌,我们告诉他抓到了什么牌。 但是这怎么在类型系统里表示呢。

方法是为同一个函数提供多个函数类型定义来进行函数重载。 编译器会根据这个列表去处理函数的调用。 下面我们来重载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会产生错误。

转载:《TypeScript 中文入门教程》 8、函数的更多相关文章

  1. 转载:TypeScript 简介与《TypeScript 中文入门教程》

    简介 TypeScript是一种由微软开发的自由和开源的编程语言.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程.安德斯·海尔斯伯格,C#的首席架构 ...

  2. 转载:《TypeScript 中文入门教程》

    缘由 事情是这样的,我想搜索 TypeScript 中文教程,结果在 https://www.baidu.com , https://cn.bing.com ,上都找不到官方的翻译,也没有一个像样的翻 ...

  3. 【转】TypeScript中文入门教程

    目录 虽然我是转载的,但看在Copy这么多文章也是很幸苦的好吧,我罗列一个目录. 转载:<TypeScript 中文入门教程> 17.注解 (2015-12-03 11:36) 转载:&l ...

  4. 《TypeScript 中文入门教程》

    转载:<TypeScript 中文入门教程> 17.注解 (2015-12-03 11:36) 转载:<TypeScript 中文入门教程> 16.Symbols (2015- ...

  5. 转载:《TypeScript 中文入门教程》 9、泛型

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性. 组件不 ...

  6. 转载:《TypeScript 中文入门教程》 17、注解

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 随着TypeScript和ES6里引入了类,现在在一些场景下我们会需要额外的特性 ...

  7. 转载:《TypeScript 中文入门教程》 14、输入.d.ts文件

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 当使用外部JavaScript库或新的宿主API时,你需要一个声明文件(.d.t ...

  8. 转载:《TypeScript 中文入门教程》 13、类型兼容性

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 TypeScript里的类型兼容性基于结构子类型的. 结构类型是只一种只使用其成 ...

  9. 转载:《TypeScript 中文入门教程》 12、类型推导

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 这节介绍TypeScript里的类型推论.即,类型是在哪里如何被推断的. 基础 ...

随机推荐

  1. PC使用网线上网的条件下,通过PC的Wifi共享提供手机上网教程

    场景和目标 你有一个笔记本(或装有无线网卡的PC),可以通过网线上网,但是没有无线路由器.现在想要通过笔记本的无线网,让手机也能共享wifi上网. 环境 Win7 操作系统.带有无线网卡的PC或笔记本 ...

  2. Servlet程序中玩验证码

    验证码思想:所谓验证码就是产生若干随机数,存放到session中,然后在servlet中获取session中的该值与页面输入值相比较,进而判断正误.   产生验证码的方法: 随机数放在图片中,封装为一 ...

  3. WCF 安全性 之 自定义证书验证

    案例下载 http://download.csdn.net/detail/woxpp/4113172 客户端调用代码 通过代理类 代理生成 参见 http://www.cnblogs.com/woxp ...

  4. Constraint4:default约束

    Default约束的作用是在insert语句执行时,如果未显式给指定的column赋值,那么使用默认值给column赋值:如果在Insert命令中显式为指定的Column赋值,那么将插入显式值.每一列 ...

  5. webpack进阶构建项目(一)

    webpack进阶构建项目(一) 阅读目录 1.理解webpack加载器 2.html-webpack-plugin学习 3.压缩js与css 4.理解less-loader加载器的使用 5.理解ba ...

  6. 一起学微软Power BI系列-官方文档-入门指南(7)发布与共享-终结篇+完整PDF文档

    接触Power BI的时间也只有几个月,虽然花的时间不多,但通过各种渠道了解收集,谈不上精通,但对一些重要概念和细节还是有所了解.在整理官方文档的过程中,也熟悉和了解了很多概念.所以从前到后把微软官方 ...

  7. AngularJS之ng-class(十一)

    前言 本节我们来讲讲指令中的ng-class,通过添加ng-class来生成对应的样式,有时候得根据不同的条件来选择对应的样式,本节我们来看看ng-class的灵活性用法. ng-class使用字符串 ...

  8. TextView+Fragment实现底部导航栏

    前言:项目第二版刚上线没多久,产品又对需求进行了大改动,以前用的是左滑菜单,现在又要换成底部导航栏,于是今天又苦逼加班了.花了几个小时实现了一个底部导航栏的demo,然后总结一下.写一篇博客.供自己以 ...

  9. java.util.concurrent包详细分析--转

    原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...

  10. C# 将PowerPoint文件转换成PDF文件

    PowerPoint的优势在于对演示文档的操作上,而用PPT查看资料,反而会很麻烦.这时候,把PPT转换成PDF格式保存,再浏览,不失为一个好办法.在日常编程中和开发软件时,我们也有这样的需要.本文旨 ...