TypeScript 函数
函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为 的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。
在 TypeScript 中,函数是用于执行特定任务并可能返回结果的代码块。
函数类型
可以为函数定义类型来明确函数的参数类型和返回值类型。
// 有名字的函数
function sum(a: number, b: number): number {return a + b;
}
// 匿名函数
let mySum = function(x: number, y: number): number { return x + y; };
这里明确了 sum
函数接受两个 number
类型的参数 a
和 b
,并返回一个 number
类型的值。
书写完整函数类型
函数类型包含两部分:参数类型和返回值类型。 当写出完整函数类型的时候,这两部分都是需要的。
在函数和返回值类型之前使用( =>
)符号。
返回值类型是函数类型的必要部分,如果函数没有返回任何值,必须指定返回值类型为 void
而不能留空。
let mySum: (x: number, y: number) => number =function(x: number, y: number): number { return x + y; };
在这段代码中:
let mySum: (x: number, y: number) => number
这部分定义了一个变量mySum
,并指定了它的类型为接受两个number
类型参数并返回number
类型值的函数类型。- 然后通过
function(x: number, y: number): number { return x + y; }
为mySum
变量赋值了一个具体的函数实现。
以参数列表的形式写出参数类型,为每个参数指定一个名字和类型:
let mySum: (a: number, b: number) => number =function(x: number, y: number): number { return x + y; };
在这段代码中,虽然函数定义中的参数名 a
、b
与函数实现中的参数名 x
、y
不同,但它们的类型都是 number
,这是匹配的。
TypeScript 在类型检查时关注的是参数的类型,而不是参数的名称。只要参数的类型符合函数类型定义的要求,代码就可以通过类型检查。
在定义函数的类型时,只考虑参数的类型和返回值的类型。
函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成API的一部分。
这是因为这些捕获变量属于函数的内部实现细节,对于使用该函数的外部代码来说,并不需要知道这些内部的状态。函数的 API 主要是通过其公开的参数和返回值来与外部进行交互和通信。
示例:
let outerValue = 10;function myFunction(x: number): number {let innerValue = 5;return x + outerValue + innerValue;
}
在上述 myFunction
函数中,outerValue
是捕获的外部变量,innerValue
是函数内部的变量,它们都不会体现在函数的类型 (x: number) => number
中。
推断类型
如果在赋值语句的一边指定了类型,但是另一边没有类型的话,TypeScript编译器会自动识别出类型:
let mySum = function(x: number, y: number): number { return x + y; };
在这个例子中,mySum
的类型会被推断为 (x: number, y: number) => number
。
可选参数和默认参数
JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined
。
TypeScript里的每个函数参数都是必须的。传递给一个函数的参数个数必须与函数期望的参数个数一致。
在 TypeScript 中,可选参数和默认参数为函数提供了更灵活的参数设置方式。
可选参数
通过在参数名后面添加 ?
来标记参数为可选参数。调用函数时,可以选择不传递该参数。
示例:
function printInfo(name: string, age?: number) {console.log(`Name: ${name}, Age: ${age ?? 'no age'}`);
}printInfo("张三"); // "Name: 张三, Age: no age"
printInfo("李四", 25); // "Name: 李四, Age: 25"
printInfo("王二", 0); // "Name: 王二, Age: 0"
可选参数必须跟在必须参数后面。
这是为了确保在调用函数时,必需的参数能够按照正确的顺序被传递,而不会因为可选参数的存在导致参数传递的混乱。
默认参数
可以为参数指定一个默认值,如果调用函数时没有传递该参数或传递的值是undefined
时,就会使用默认值。
function printInfo(name: string, age: number = 18) {console.log(`Name: ${name}, Age: ${age}`);
}
printInfo("张三"); // "Name: 张三, Age: 18"
printInfo("李四", 25); // "Name: 李四, Age: 25"
printInfo("王二", undefined); // "Name: 王二, Age: 18"
当一个参数在所有必需参数之后并且有默认初始化值时,它就具有了可选的性质。
在 printInfo
函数中,age
参数具有默认值 18
,所以在调用函数时可以省略它,就像可选参数一样。
与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined
值来获得默认值。
示例:
function printInfo(name: string = "张三", age: number) {console.log(`Name: ${name}, Age: ${age}`);
}
printInfo(undefined, 18); // "Name: 张三, Age: 18"
printInfo("李四", 25); // "Name: 李四, Age: 25" // 假如只传入 age
printInfo(25); // Error: Expected 2 arguments, but got 1.
剩余参数
必要参数、默认参数、可选参数有个共同点:它们表示某一个参数。
有时,想同时操作多个参数,或者并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments
来访问所有传入的参数。
在 TypeScript 中,剩余参数用于表示不确定数量的参数。它使用 ...
语法来定义。
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。
function sumAll(numbers: number[], ...restNum: number[]) {let total = 0;for (const num of numbers) {total += num;}for (const num of restNum) {total += num;}return total;
}// 没有剩余参数
console.log(sumAll([1, 2, 3])); // 6// 有 3 个剩余参数
console.log(sumAll([1, 2, 3], 4, 5, 6)); // 21
在为带有剩余参数的函数定义类型时,也需要正确地表示剩余参数的部分。
实现 sumAll
函数的类型定义:
let myFunction: (numbers: number[], ...restNum: number[]) => number = sumAll;// 参数名字可以不一样,但是参数的类型必须一样
let myFunc: (nums: number[], ...rest: number[]) => number = sumAll;
this和箭头函数
在普通函数中,this
的值取决于函数的调用方式。如果是作为对象的方法被调用,this
指向该对象;如果是独立调用,非严格模式下,this
通常指向全局对象(在浏览器环境中是 window
,在 Node.js
环境中是 global
)。严格模式下, this
为 undefined
。
非严格模式下:
// 作为对象的方法被调用
let obj = {method: function() {console.log(this); // 这里的 this 指向 obj 对象}
};
obj.method();// 独立调用
function standalone() {console.log(this); // 在浏览器中,这里的 this 指向 window 对象
}
standalone();
严格模式下:
// 作为对象的方法被调用
let strictObj = {strictMethod: function() {"use strict";console.log(this); // 这里的 this 指向 strictObj 对象}
};
strictObj.strictMethod();// 独立调用
function strictStandalone() {"use strict";console.log(this); // 这里的 this 是 undefined
}
strictStandalone();
可以在浏览器中执行代码来验证结果。
箭头函数中的 this
继承自其定义时所在的上下文,不会因为调用方式的不同而改变。
let obj = {method: () => {// "use strict";console.log(this); // 在浏览器中,这里的 this 指向 window 对象}
};
obj.method();
这段代码中,无论是严格模式还是非严格模式,this
都指向 window
对象。
箭头函数 method
是在对象 obj
中定义的,由于是箭头函数,this
并不指向 obj
,而是继承了外部(即全局)的 this
值。在浏览器环境中,全局的 this
就是 window
对象。
使 this
指向obj
:
let obj = {width: 10,height: 10,method() {return () => {return console.log(this)}}
};
obj.method()();// 把obj.method()赋值给method变量
let method = obj.method();
method()
由上图运行结果可知,箭头函数里的 this
指向obj
。此时,this
的 类型 是 any
。
this
参数
在 TypeScript 中,this
可以被显式地作为参数添加到函数的参数列表中,用于更明确地控制和处理函数内部的 this
指向。
提供一个显式的 this
参数, 让this
的类型不是any
:
interface OBJ {width: number;height: number;method(this: OBJ): () => number;
}let obj: OBJ = {width: 10,height: 10,method(this: OBJ) {return () => {return this.width + this.height}}
};
console.log(obj.method()()); // 20let method = obj.method();
console.log(method); // 20
这段代码中,this
是 OBJ
类型的。不论如何调用method()
,this
始终指向OBJ
。
this
参数在回调函数里
当把一个函数作为回调传递给库函数,并且在回调函数中使用了 this
时,确实经常会遇到 this
指向错误或为 undefined
的问题。
这是因为函数的调用方式会影响 this
的指向。如果回调函数被库函数以普通函数的方式调用,而不是作为对象的方法调用,那么 this
就不会指向预期的对象,通常会是 undefined
。
例如,假设有一个库函数 doAsyncOperation
,它接受一个回调函数:
function doAsyncOperation(callback: () => void) {// 模拟异步操作完成后调用回调setTimeout(callback, 1000);
}
然后有一个类 MyClass
,其中有一个方法想要在回调中使用 this
:
class MyClass {data: string = "Some data";performOperation() {doAsyncOperation(this.someCallback);}someCallback() {console.log(this.data); // undefined}
}
let myC = new MyClass();
myC.performOperation();
执行myC.performOperation()
语句, doAsyncOperation
内部以普通函数的方式调用 callback
,当 doAsyncOperation
调用回调函数时,this
不是指向 MyClass
的实例,而是 undefined
,导致无法访问 data
属性。回调函数的输出结果为undefined
。
因为在 setTimeout
内部,如果传递的回调函数是一个普通函数,那么 this
的指向取决于运行环境和调用方式。
在非严格模式下,如果是在浏览器环境中,this
通常指向 window
对象;在 Node.js
环境中,this
通常指向 global
对象。
在严格模式下,this
的值会是 undefined
。
把someCallback
改为箭头函数:
class MyClass {data: string = "Some data";performOperation() {doAsyncOperation(this.someCallback);}someCallback = () => {console.log(this.data); // "Some data"}
}
let myC = new MyClass();
myC.performOperation();
在上述代码中,someCallback
被定义为箭头函数。
箭头函数不会创建自己的 this
上下文,而是继承外层(即定义它的 MyClass
实例)的 this
上下文。
当 doAsyncOperation
函数中的异步操作完成并调用 callback
(即 this.someCallback
)时,由于 someCallback
是箭头函数,它所使用的 this
仍然是其定义时所在的 MyClass
实例的 this
。
所以,在 someCallback
函数内部,能够正确访问到 MyClass
实例的属性 data
,并将其打印出来,即输出结果为 “Some data” 。
或者:
class MyClass {data: string = "Some data";performOperation() {doAsyncOperation(() => this.someCallback());}someCallback() {console.log(this.data); // "Some data"}
}
let myC = new MyClass();
myC.performOperation();
在 doAsyncOperation(() => this.someCallback());
中,使用了一个立即执行的箭头函数。
箭头函数不会创建自己的 this
上下文,而是继承外层(即定义它的 MyClass
实例)的 this
上下文。
在这个箭头函数内部,this
正确地指向了 MyClass
的实例。
上面这个例子比较简单,再举一个复杂一点的例子。
例如,假设有一个库函数 UIElement
,库函数的作者要指定 this
的类型:
interface UIElement {addClickListener(onclick: (this: void, e: CustomEvent, params?: {[propName: string]: any}) => void): void;
}
有一个Handler
类,处理自定义事件以及在不同类型的回调函数中处理数据和 this
的指向。
class Handler {info: string = "";onClickGood(this: void, e: CustomEvent) {// 通过指定 this: void ,表明该回调函数在被调用时不期望或不依赖于特定的 this 上下文。console.log(this); // undefinedconsole.log("clicked!");}onClickGood2 = (e: CustomEvent) => {// 箭头函数,this 指向 Handlerthis.info = e.detail.message;};doOther (this: void, e: CustomEvent, params?: {[propName: string]: any}) {// 在这里根据传入的 params 进行相应的处理// 如果要操作this,就使用箭头函数。console.log(e.detail.message);console.log(params);};
}let h = new Handler();
let uiElement: UIElement = {addClickListener(onclick) {const event = new CustomEvent("click", { detail: { message: "Hello" } });onclick(event);}
};uiElement.addClickListener(h.onClickGood);
uiElement.addClickListener(h.onClickGood2);let uiElementDo: UIElement = {addClickListener(onclick) {const event = new CustomEvent("click", { detail: { message: "输出用户的输入" } });let word = prompt("输入你想说的话", "");let params = {word: word}onclick(event, params);}
};uiElementDo.addClickListener(h.doOther)
函数重载
在 TypeScript 中,函数重载是指可以为同一个函数定义多个不同的签名(参数类型和返回类型的组合)。
示例:
function add(a: number, b: number): number;
function add(a: string, b: string): string;function add(a: any, b: any): any {if (typeof a === 'number' && typeof b === 'number') {return a + b;}if (typeof a ==='string' && typeof b ==='string') {return `${a} ${b}`;}throw new Error('Invalid parameters');
}console.log(add(1, 2)); // 3
console.log(add('hello', 'world')); // "hello world"
在上述示例中,add
函数有两个不同的函数签名:一个接受两个数字参数并返回一个数字,另一个接受两个字符串参数并返回一个字符串。
函数的实现部分根据传入参数的类型来决定执行不同的逻辑。
相关文章:

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

C++ : namespace,输入与输出,函数重载,缺省参数
一,命名空间(namespace) 1.1命名空间的作用与定义 我们在学习c的过程中,经常会碰到命名冲突的情况。就拿我们在c语言中的一个string函数来说吧: int strncat 0; int main() {printf("%d", strncat);return 0; } 当我们运行之后&…...

目标检测 | yolov1 原理和介绍
1. 简介 论文链接:https://arxiv.org/abs/1506.02640 时间:2015年 作者:Joseph Redmon 代码参考:https://github.com/abeardear/pytorch-YOLO-v1 yolo属于one-stage算法,仅仅使用一个CNN网络直接预测不同目标的类别与…...

excel中有些以文本格式存储的数值如何批量转换为数字
一、背景 1.1 文本格式存储的数值特点 在平时工作中有时候会从别地方导出来表格,表格中有些数值是以文本格式存储的(特点:单元格的左上角有个绿色的小标)。 1.2 文本格式存储的数值在排序时不符合预期 当我们需要进行排序的时候…...

原神升级计划数据表:4个倒计时可以修改提示信息和时间,可以点击等级、命座、天赋、备注进行修改。
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>原神倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;body {background: #0b1b2c;}}header {width: 100vw;heigh…...

YoloV10 论文翻译(Real-Time End-to-End Object Detection)
摘要 近年来,YOLO因其在计算成本与检测性能之间实现了有效平衡,已成为实时目标检测领域的主流范式。研究人员对YOLO的架构设计、优化目标、数据增强策略等方面进行了探索,并取得了显著进展。然而,YOLO对非极大值抑制࿰…...

第R1周:RNN-心脏病预测
本文为🔗365天深度学习训练营 中的学习记录博客 原作者:K同学啊 要求: 1.本地读取并加载数据。 2.了解循环神经网络(RNN)的构建过程 3.测试集accuracy到达87% 拔高: 1.测试集accuracy到达89% 我的环境&a…...

Golang | Leetcode Golang题解之第321题拼接最大数
题目: 题解: func maxSubsequence(a []int, k int) (s []int) {for i, v : range a {for len(s) > 0 && len(s)len(a)-1-i > k && v > s[len(s)-1] {s s[:len(s)-1]}if len(s) < k {s append(s, v)}}return }func lexico…...

远程连接本地虚拟机失败问题汇总
前言 因为我的 Ubuntu 虚拟机是新装的,并且应该装的是比较纯净的版本(纯净是指很多工具都尚未安装),然后在使用远程连接工具 XShell 连接时出现了很多问题,这些都是我之前没遇到过的(因为之前主要使用云服…...

WebRTC 初探
前言 项目中有局域网投屏与文件传输的需求,所以研究了一下 webRTC,这里记录一下学习过程。 WebRTC 基本流程以及概念 下面以 1 对 1 音视频实时通话案例介绍 WebRTC 的基本流程以及概念 WebRTC 中的角色 WebRTC 终端,负责音视频采集、编解码、NAT 穿…...

Python:read,readline和readlines的区别
在Python中,read(), readline(), 和 readlines() 是文件操作中常用的三个方法,它们都用于从文件中读取数据,但各自的使用方式和适用场景有所不同。 read() 方法: read(size-1) 方法用于从文件中读取指定数量的字符。如果指定了si…...

重生之我学编程
编程小白如何成为大神?大学新生的最佳入门攻略 编程已成为当代大学生的必备技能,但面对众多编程语言和学习资源,新生们常常感到迷茫。如何选择适合自己的编程语言?如何制定有效的学习计划?如何避免常见的学习陷阱&…...

如何将PostgreSQL的数据实时迁移到SelectDB?
PostgreSQL 作为一个开源且功能强大的关系型数据库管理系统,在 OLTP 系统中得到了广泛应用。很多企业利用其卓越的性能和灵活的架构,应对高并发事务、快速响应等需求。 然而对于 OLAP 场景,PostgreSQL 可能并不是最佳选择。 为了实现庞大规…...

关于c语言的const 指针
const * type A 指向的数据是常量 如上所示,运行结果如下,通过解引用的方式,改变了data的值 const type * A 位置是常量,不能修改 运行结果如下 type const * A 指针是个常量,指向的值可以改变 如上所示,…...

万能门店小程序开发平台功能源码系统 带完整的安装代码包以及安装搭建教程
互联网技术的迅猛发展和用户对于便捷性需求的不断提高,小程序以其轻量、快捷、无需安装的特点,成为了众多商家和开发者关注的焦点。为满足广大商家对于门店线上化、智能化管理的需求,小编给大家分享一款“万能门店小程序开发平台功能源码系统…...

C#初级——字典Dictionary
字典 字典是C#中的一种集合,它存储键值对,并且每个键与一个值相关联。 创建字典 Dictionary<键的类型, 值的类型> 字典名字 new Dictionary<键的类型, 值的类型>(); Dictionary<int, string> dicStudent new Dictionary<int, str…...

git版本控制的底层实现
目录 前言 核心概念串讲 底层存储形式探测 本地仓库的详细解析 提交与分支的深入解析 几个问题的深入探讨 前言 Git的重要性 Git是一个开源的版本控制工具,广泛用于编程开发领域。它极大地提高了研发团队的开发协作效率。对于开发者来说,Git是一个…...

深入解析数据处理的技术与实践
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。 ⭐…...

python-调用c#代码
环境: win10,net framework 4,python3.9 镜像: C#-使用IronPython调用python代码_ironpython wpf-CSDN博客 https://blog.csdn.net/pxy7896/article/details/119929434 目录 hello word不接收参数接收参数 其他例子 hello word 不…...

构建铁路安全防线:EasyCVR视频+AI智能分析赋能铁路上道作业高效监管
一、方案背景 随着我国铁路特别是高速铁路的快速发展,铁路运营里程不断增加,铁路沿线的安全环境对保障铁路运输的安全畅通及人民群众的生命财产安全具有至关重要的作用。铁路沿线安全环境复杂多变,涉及多种风险因素,如人员入侵、…...

openai command not found (mac)
题意:mac 系统上无法识别 openai 的命令 问题背景: Im trying to follow the fine tuning guide for Openai here. 我正在尝试遵循 OpenAI 的微调指南 I ran: 我运行以下命令 pip install --upgrade openaiWhich install without any errors.…...

鸿蒙(API 12 Beta2版)NDK开发【LLDB高性能调试器】调试和性能分析
概述 LLDB(Low Level Debugger)是新一代高性能调试器。 当前HarmonyOS中的LLDB工具是在[llvm15.0.4]基础上适配演进出来的工具,是HUAWEI DevEco Studio工具中默认的调试器,支持调试C和C应用。 工具获取 可通过HUAWEI DevEco S…...

HAL库源码移植与使用之DMA
内存到内存不支持传输计数器自动重装 结构: 与DMA具有连线的外设都可以完成搬运 DMA触发源 DMA优先级分配 由仲裁器来决定 寄存器作用: DMA.C #include "./BSP/DMA/dma.h" #include "./SYSTEM/delay/delay.h"DMA_HandleTypeDef…...

Scrapy爬虫框架介绍、创建Scrapy项目
Scrapy官网:https://scrapy.org/ 什么是Scrapy Scrapy 是一个基于 Python 的快速的高级网页抓取和网页爬取框架,用于抓取网站并从其页面中提取结构化数据。它可用于多种用途,从数据挖掘到监控和自动化测试。 Scrapy核心组件 1. Scrapy Engin…...

如何监测某个进程是否退出(C++)?
使用WaitForSingleObject函数,可以判断进程是否退出。 WaitForSingleObject函数的作用是:等待直到指定的对象处于信号状态(通知状态)或到达指定的等待时间(超时时间)。 函数声明如下: 1 DWOR…...

Python:Neo 库读取 ABF 文件,数据格式详解
Neo 库读取 ABF 文件后的数据格式 neo 是一个用于处理电生理数据的 Python 库,支持多种数据格式,包括 ABF 文件。了解 neo 读入 ABF 文件后的数据结构非常重要,以下给大家介绍一下使用 neo 读取 ABF 文件,及其对象格式。 1. ABF…...

【Linux】网络基础_3
文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础 5. socket编程 socket 常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服…...

C++之从C过渡(上)
C之从C过渡 前言 暂时告别C语言,我们走进C。对于有C语言基础,初学C的我们来说,在正式学习C的主体内容之前,我们需要先有一个过渡,本文中会总结过渡需要了解的零散知识,主要是语法。 正文 C的第一个程序 …...

MongoDB 100问
基础问题 1. 什么是MongoDB? MongoDB是一种面向文档的NoSQL数据库,使用BSON(二进制JSON)格式存储数据。它支持动态模式设计,具有高性能、高可用性和易扩展性。 2. MongoDB和传统关系型数据库的区别是什么?…...

Arduino ESP32使用 HardwareSerial创建一个任意串口
文章目录 前言使用 `HardwareSerial` 创建任意串口创建 `HardwareSerial` 对象示例代码`begin` 函数总结前言 在 Arduino 项目中,串口通信是一种常见且重要的通信方式。ESP32 作为一款功能强大的微控制器,提供了多个 UART(通用异步收发传输器)接口,允许用户灵活地进行串口…...