第三天:
1、函数的声明和调用
函数的基础使用和js基本没什么区别,按照寻常使用即可
通常我们在创建函数的时候会 显式注解参数,虽然也可以用ts内部自己推导出类型,但是大多数情况下无法推导出参数的类型。
函数的返回类型也可以推导出来,也可以显示注解
//五种声明函数的方式:
//具名函数
function add(a:number,b:number){
return a+b
}
//函数表达式
let grate = function (name:string) {
return "hello"
}
//箭头函数表达式
let grates = (name:string)=>{
return "hello"
}
//箭头函数表达式简写
let gratess = (name:string)=>"hello"
//构造函数
let greet5 = new Function('name','return "hello"+name')
函数在调用的时候,直接传入实参就行,Ts将自己去检查是否和函数定义的形参的类型是否兼容
如果传入的参数类型有误或者没有传入相应的参数,Ts会指出问题
1.1可选和默认的参数
和对象与元祖类型一样,可以使用 **?**来将参数标记成可选项,即调用时候该参数可以不传
PS:声明函数参数时候,必须传的参数要放在前面,随后才可以是可选的参数!!!
function add(a:number,b?:number){ return a }
可以为可选参数提供默认值,和可选的区别是
带默认值的参数不要求放在参数列表的末尾,而可选参数必须放在末尾
type Context = { appId?:string, userId?:string } function log(message:string,context:Context={}) { return message+context }
两者比较而言,更常用的是默认参数
1.2剩余参数
往往在js中,当不知道参数的详细时,我们都使用arguments来当作参数对象,但是在ts中,如果使用arguments就会有个比较大的问题,失去的ts特有的安全性,Ts无法推导出arguments中的正确类型,会都变成any,因此,为了在Ts中确保参数安全,可以使用以下方法来使用剩余参数。
PS:注意!!!一个函数最多最多只能由一个剩余参数,而且必须位于参数列表的最后!!!
function sum(...numbers:number[]):number {
return numbers.reduce((item,i)=>item+i,0)
}
1.3call,apply,bind
emmmmm,不知道记点啥,和js用法貌似没有区别,就注意一下传参的方式就行
sum(10,20) sum.call(null,10,20) sum.apply(null,[10,20]) sum.bind(null,10,20)()
1.4注解this的类型
JS中this的指向我个人理解是谁调用,就指向谁
在Ts中,函数如果使用this,在函数的第一个参数声明this的类型,即放在其他参数前面,这样在每次调用的时候,Ts都会确保this确实是你想要的预期类型。
需要注意的是,this不是常规的定义的参数,只是个保留字,是函数签名的一部分。
function fancyDate(this:Date) {
return `${this.getDate()}`
}
fancyDate.call(new Date)
1.5生成器函数
生成器可以精确控制生成什么值,它是惰性的,只有在使用方要求时才会计算下一个值(即调用next())
1、函数名称前面的星号(*)表明这是一个生成器函数,调用生成器返回一个可迭代的迭代器。
2、生成器可以一直产生数值。
3、生成器使用yield关键字产出值,输出的value就是yield定义的值,每一次调用.next()时候就会生成一个值。
4、生成器也可以显式注解,把产出值的类型放在后面
function* createFibonacciGenerator():IterableIterator<number> {//1 /4
let a = 0;
let b = 1;
while (true) {//2
yield a;//3
[a,b] = [b,a+b];
}
}
let fibonacciGenerator = createFibonacciGenerator();
fibonacciGenerator.next()//{value:0,done:false}
fibonacciGenerator.next()//{value:1,done:false}
fibonacciGenerator.next()//{value:1,done:false}
fibonacciGenerator.next()//{value:2,done:false}
fibonacciGenerator.next()//{value:3,done:false}
fibonacciGenerator.next()//{value:5,done:false}
1.6迭代器
迭代器是生成器的相对面,生成器是生成一系列值的方式,而迭代器是使用这些值的方式,调用生成器返回一个可迭代的迭代器
迭代器具有Symbol.iterator属性,也有next()方法
下面的numbers就是一个迭代器,调用后返回一个可迭代的迭代器
let numbers = {
*[Symbol.iterator](){
for (let i = 0; i < 10; i++) {
yield i
}
}
}
//调用
numbers[Symbol.iterator]()
1.7调用签名
函数的调用签名 只包括类型层面的代码,即只有类型定义,没有值。
调用签名没有函数的定义主体,因此也就无法推导出函数的返回类型,因此必须 显示注解。、
拓展
什么是类型层面和值层面代码
一个有效的JS代码就是值层面的代码,
一个有效的Ts代码但不是有效的JS代码,就是类型层面代码
// 签名
type Log = (message:string,userId?:string)=>void
// 调用签名
let log:Log = (
message,
userId = "nonono"
)=>{
let time = new Date().toISOString();
console.log(time,message,userId);
}
log("hello")
注意函数类型定义的两种写法
// 简写签名
type Log = (message:string,userId?:string)=>void
//完整写法
type Log = {
(message:string,userId?:string):void
}
1.8函数类型重载(没学懂,先记录下)
什么是重载函数 ,一句话:有多个调用签名的函数
Ts支持动态重载函数声明,而且函数的输出类型取决于输入类型。
type Reservation = any
//定义两个签名,是否存在to参数
type Reserve = {
(from:Date,to:Date,destination:string):Reservation
(from:Date,destination:string):Reservation
}
let reserve : Reserve=(
from:Date,
toOrDestination:Date|string,
destination:string
)=>{
if(toOrDestination instanceof Date && destination!== undefined){
// ...
}else if(typeof toOrDestination === "string"){
// ...
}
}
遇到的问题:
1、函数类型重载,完全没体会到什么作用,如果只是对于参数的使用,为什么不通过对to参数用个?来实现是否传参呢