TypeScript Function(函数)

简介: 在JavaScript中,函数是构成任何应用程序的基础块。通过函数,你得以实现建立抽象层、模仿类、信息隐藏和模块化。在TypeScript中,虽然已经存在类和模块化,但是函数依旧在如何去"处理"事件的问题上起关键作用。

在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"并且传入其他类型的参数将会导致错误。

相关文章
|
5月前
|
人工智能 JavaScript 程序员
一文彻底搞明白HarmonyOS基础TypeScript中的泛型函数
程序员Feri是一位拥有12年+经验的技术专家,擅长嵌入式、鸿蒙、人工智能和Java等领域。本文深入探讨TypeScript中的泛型函数,涵盖基础语法、类型约束、高级技巧及应用场景。通过泛型函数,实现代码逻辑与具体类型的解耦,提升类型安全性和复用性。内容包括集合操作、API抽象、工具类开发等实际应用,以及条件类型、默认类型参数和工具类型的高级技巧。最后提醒开发者注意过度泛型化和性能权衡问题,总结泛型函数在TypeScript类型系统中的核心地位及其未来发展方向。
99 1
一文彻底搞明白HarmonyOS基础TypeScript中的泛型函数
|
5月前
|
人工智能 Python
083_类_对象_成员方法_method_函数_function_isinstance
本内容主要讲解Python中的数据类型与面向对象基础。回顾了变量类型(如字符串`str`和整型`int`)及其相互转换,探讨了加法在不同类型中的表现。通过超市商品分类比喻,引出“类型”概念,并深入解析类(class)与对象(object)的关系,例如具体橘子是橘子类的实例。还介绍了`isinstance`函数判断类型、`type`与`help`探索类型属性,以及`str`和`int`的不同方法。最终总结类是抽象类型,对象是其实例,不同类型的对象有独特运算和方法,为后续学习埋下伏笔。
102 7
083_类_对象_成员方法_method_函数_function_isinstance
|
5月前
|
Python
[oeasy]python086方法_method_函数_function_区别
本文详细解析了Python中方法(method)与函数(function)的区别。通过回顾列表操作如`append`,以及随机模块的使用,介绍了方法作为类的成员需要通过实例调用的特点。对比内建函数如`print`和`input`,它们无需对象即可直接调用。总结指出方法需基于对象调用且包含`self`参数,而函数独立存在无需`self`。最后提供了学习资源链接,方便进一步探索。
108 17
|
5月前
|
人工智能 Python
[oeasy]python083_类_对象_成员方法_method_函数_function_isinstance
本文介绍了Python中类、对象、成员方法及函数的概念。通过超市商品分类的例子,形象地解释了“类型”的概念,如整型(int)和字符串(str)是两种不同的数据类型。整型对象支持数字求和,字符串对象支持拼接。使用`isinstance`函数可以判断对象是否属于特定类型,例如判断变量是否为整型。此外,还探讨了面向对象编程(OOP)与面向过程编程的区别,并简要介绍了`type`和`help`函数的用法。最后总结指出,不同类型的对象有不同的运算和方法,如字符串有`find`和`index`方法,而整型没有。更多内容可参考文末提供的蓝桥、GitHub和Gitee链接。
107 11
|
12月前
|
JavaScript
typeScript基础(3)_ts函数默认值和可选参数
本文介绍了在TypeScript中如何使用函数的默认值和可选参数。展示了如何为函数参数指定默认值,使得在调用函数时可以省略某些参数,以及如何定义可选参数。
783 2
|
11月前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新特性,与传统函数相比,它有更简洁的语法,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。箭头函数不适用于构造函数,不能使用new关键字调用。
|
11月前
|
移动开发 JavaScript 前端开发
TypeScript:数组类型&函数使用&内置对象
本文介绍了 TypeScript 中的数组类型、对象数组、二维数组、函数、函数重载、内置对象等概念,并通过代码示例详细展示了它们的使用方法。还提供了一个使用 HTML5 Canvas 实现的下雨效果的小案例。
196 1
|
10月前
|
中间件 Docker Python
【Azure Function】FTP上传了Python Function文件后,无法在门户页面加载函数的问题
通过FTP上传Python Function至Azure云后,出现函数列表无法加载的问题。经排查,发现是由于`requirements.txt`中的依赖包未被正确安装。解决方法为:在本地安装依赖包到`.python_packages/lib/site-packages`目录,再将该目录内容上传至云上的`wwwroot`目录,并重启应用。最终成功加载函数列表。
102 0
|
11月前
|
数据可视化 开发者 索引
详解Wireshark LUA插件函数:function p_myproto.dissector(buffer, pinfo, tree)
在 Wireshark 中,LUA 插件通过 `function p_myproto.dissector(buffer, pinfo, tree)` 扩展协议解析能力,解析自定义应用层协议。参数 `buffer` 是 `PacketBuffer` 类型,表示原始数据包内容;`pinfo` 是 `ProtoInfo` 类型,包含数据包元信息(如 IP 地址、协议类型等);`tree` 是
444 1
|
11月前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新语法,相比传统函数表达式更简洁,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。这使得箭头函数在处理回调和闭包时更加灵活方便。

热门文章

最新文章