1 function student(x:string,y:number):string{ 2 return `我是${x},今年${y}岁`; 3 } 4 5 console.log(student("wzy",22)); // 我是wzy,今年22岁
形式与JavaScript中的函数声明相同,有什么区别?
(1)指定参数的类型(因为有类型检测,所以必须遵循),然后指定返回值的类型,
这时返回值类型可以省略,因为Typescript会根据返回语句手动推导出返回值类型。
(2)参数不能多也不能少,恰到好处,且与顺序有关。
1 function student(x:string,y:number){ 2 console.log(`我是${x},今年${y}岁`); 3 } 4 5 student(22,"wzy"); 6 // Argument of type '22' is not assignable to parameter of type 'string'.
2. 函数表达式
有些词总是需要复制的,比如下面两句话
如果我们今天要写一个函数表达式(Function Expression)的定义,可能会这样写:
1 let student = function(x:string,y:number):string{ 2 return `我是${x},今年${y}岁`; 3 } 4 5 console.log(student("wzy",22)); // 我是wzy,今年22岁
这样就可以编译通过了,但实际上,上面的代码只是定义了等号右边匿名函数的类型,而等号右边的同学是通过形参运算进行类型推导来确定的。
如果我们需要自动给students添加类型,应该是这样的:
1 let student:(x:string,y:number)=>string = function(x:string,y:number):string{ 2 return `我是${x},今年${y}岁`; 3 } 4 5 console.log(student("wzy",22)); // 我是wzy,今年22岁
这还需要多说几句:
(1)前后参数名称可以不一致
1 let student:(x:string,y:number)=>string = function(name:string,age:number):string{ 2 return `我是${x},今年${y}岁`; 3 } 4 // 这样对吗?嗯!好像是哪里不太对~ 5 // Cannot find name 'x'. 6 // Cannot find name 'y'. 7 8 let student:(x:string,y:number)=>string = function(name:string,age:number):string{ 9 return `我是${name},今年${age}岁`; 10 } 11 // 这样就对了~
(2)我对这些声明函数的形式的理解是:
let student : (x:string,y:number)=>string = function(name:string,age:number):string{} 声明变量 指定函数类型 根据指定函数类型定义函数 此时再根据之前讲过的“类型推论”,就有以下这样的声明方式了
1 let student:(x:string,y:number)=>string = function(name,age){ 2 return `我是${name},今年${age}岁`; 3 } 4 5 let student = function(name:string,age:number):string{ 6 return `我是${name},今年${age}岁`; 7 }
据说这叫“按上下文分类”typescript函数类型,是类型推断的一种。
(3)我们之前也说过,没有返回值的时候怎么办,在这些完整的函数声明的情况下,返回值不能为空,这时候我们都会想到void
1 let student:(x:string,y:number) = function(name,age){ 2 console.log(`我是${name},今年${age}岁`); 3 } 4 5 // '=>' expected. 6 // 这样是不允许的,也不要妄想 =>"" 这样,人家就没有""这种类型好吗 7 8 let student:(x:string,y:number)=>void = function(name,age){ 9 console.log(`我是${name},今年${age}岁`); 10 } 11 // 还是老老实实的这样子吧
3.socket中函数的定义
1 interface Student{ 2 (x:string,y:number):string 3 } 4 5 let str1:Student; 6 str1 = function(x,y){ 7 return `我是${x},今年${y}岁`; 8 }
4、可选参数
前面说过,函数的参数是必不可少的,但是也可以像socket一样使用吗?表示可选参数
1 function student(name:string,age:number,sex?:boolean){ 2 if(sex){ 3 return `name:${name},age:${age},sex:女`; 4 }else{ 5 return `name:${name},age:${age},sex:未知`; 6 } 7 } 8 9 console.log(student("weizeyang",22,true)); // name:weizeyang,age:22,sex:女 10 console.log(student("weizeyang",22)); // name:weizeyang,age:22,sex:未知
我们来看看它需要注意什么:
(1) 可选参数应放在必需参数之前
(2) 胃痛、不开心
5、默认参数
在打字稿中,您可以为函数中的参数设置默认值。 当用户不传递该参数或者传递的值未定义时,将显示默认值。
1 function student(name:string,age:number,sex="女"){ 2 return `name:${name},age:${age},sex:${sex}`; 3 } 4 5 console.log(student("weizeyang",22,"未知")); 6 // name:weizeyang,age:22,sex:未知 7 8 console.log(student("weizeyang",22)); 9 // name:weizeyang,age:22,sex:女 10 11 console.log(student("weizeyang",22,undefined)); 12 // name:weizeyang,age:22,sex:女
各个年龄段的注意事项:
(1) 可以看出,默认值的参数被识别为可选参数,但不需要位于必需参数的前面。
(2)虽然是可选参数,但并不需要放在必需参数前面,但是一旦放在必需参数上面,就必须明确写上undefined或者一个值,即不能是空的。
这样,写在上面就不是可选参数了。
1 function student(sex="女",name:string,age:number){ 2 return `name:${name},age:${age},sex:${sex}`; 3 } 4 5 console.log(student("未知","weizeyang",22)); 6 // name:weizeyang,age:22,sex:未知 7 8 console.log(student("weizeyang",22)); 9 // Expected 3 arguments, but got 2. 10 11 console.log(student(undefined,"weizeyang",22)); 12 // name:weizeyang,age:22,sex:女
我不知道其他人是否能理解这句话,但无论如何我明白它在说什么。
6. 其余参数
上面提到的必选参数、默认参数、可选参数都代表了某个参数。 有时候,你想同时操作多个参数,或者你不知道要传入多少个参数。
在 JavaScript 中,您可以使用参数来访问所有传入参数。
在打字稿中,您可以将所有参数收集到一个变量中:
1 function student(age:number,sex:boolean,...name:string[]){ 2 return `我是${name.join("")},年龄${age}`; 3 } 4 5 console.log(student(22,true,"wei","ze","yang")); 6 // 我是weizeyang,年龄22
来来来,注意一下:
(1) 其余参数将被视为无限数量的可选参数,可以是无,也可以是任意数量。 还要在所需参数之前。
(2)它是一个链表类型,名称是省略号之前的数组名,这个链表可以在函数体中使用。
(3)调用函数时,不要传入链表,只要一一传入即可。
7. Lambda 表达式及其使用
在 JavaScript 中,只有在调用函数时才能指定 this 的值。 学过JavaScript的人都知道,这是一个令人头疼的问题!
1 var student = { 2 names: ["wzy", "wyy", "wxy", "yz"], 3 getName: function() { 4 5 return function(){ 6 var item = Math.floor(Math.random() * 4); 7 console.log(this); 8 9 return { 10 name: this.names[item] // Cannot read property 'names' of undefined 11 } 12 } 13 } 14 } 15 16 let sname = student.getName(); 17 console.log("my name is " + sname().name);
复制的第7行是window,因为这个没有动态绑定
1 "use strict"; // 严格模式 2 3 var student = { 4 names: ["wzy", "wyy", "wxy", "yz"], 5 getName: function() { 6 7 return function(){ 8 var item = Math.floor(Math.random() * 4); 9 console.log(this); // undefined 10 11 return { 12 name: this.names[item] // Cannot read property 'names' of undefined 13 } 14 } 15 } 16 } 17 18 let sname = student.getName(); 19 console.log("my name is " + sname().name);
第9行被复制出来为undefined,看起来和上一行是一样的,但实际上现在是JavaScript严格模式(我们一直都是),打印出来的是undefined。
为了解决这个问题,我们可以在函数返回时绑定正确的this。 这样,无论你以后如何使用它,它都会引用绑定的“student”对象。
我们将函数表达式更改为使用 lambda 表达式 ( () => {} )。 这将在创建函数时指定“this”值,而不是在调用函数时指定。
1 var student = { 2 names: ["wzy", "wyy", "wxy", "yz"], 3 getName: function() { 4 console.log(this); 5 // 注意:下面这行是一个lambda,允许我们提前捕捉它 6 return () => { 7 var item = Math.floor(Math.random() * 4); 8 console.log(this); 9 10 return { 11 name: this.names[item] 12 } 13 } 14 } 15 } 16 17 let sname = student.getName(); 18 console.log("my name is " + sname().name);
将第4、8行复制出来
也就是学生的对象,你就完成了
8. 超载
举个栗子:
1 function schools(type:number|string):number|string{ 2 if(typeof type === "number"){ 3 return type.toFixed(2); 4 }else if(typeof type === "string"){ 5 return type.substring(0,1); 6 } 7 } 8 9 console.log(schools(1)); // 1.00 10 console.log(schools("wzy")); // w
上述函数的参数和返回值的类型都使用了联合类型,所以我们无法准确表达我传入的是数字类型,而返回值也必须是数字类型。
这时候就引入了重载的概念。
学习Java时,重载是这样的:函数名相同,函数的参数列表不同(包括参数个数和参数类型),返回值类型可以相同也可以不同。
网上的例子如下:
1 function schools(type:number):number; 2 function schools(type:string):string; 3 function schools(type:number|string):number|string{ 4 if(typeof type === "number"){ 5 return type.toFixed(2); 6 }else if(typeof type === "string"){ 7 return type.substring(0,1); 8 } 9 } 10 11 console.log(schools(1)); // 1.00 12 console.log(schools("wzy")); // w
也就是说这两个函数之前已经被声明过好几次了
必须要注意的是:
(1)TypeScript会优先从第一个函数定义开始匹配,所以如果多个函数定义存在包含关系typescript函数类型,需要先写出精确的定义。
再举个栗子:
1 function schools(type:number):number; 2 function schools(type:string):string; 3 function schools(type:number | string | boolean):number | string | boolean{ 4 if(typeof type === "number"){ 5 return type.toFixed(2); 6 }else if(typeof type === "string"){ 7 return type.substring(0,1); 8 }else if(typeof type === "boolean"){ 9 return true; 10 } 11 } 12 13 console.log(schools(1)); // 1.00 14 console.log(schools("wzy")); // w 15 console.log(schools(true)); 16 // Argument of type 'true' is not assignable to parameter of type 'string'.
(2)说明第3行的代码不属于重载部分,这里只有第1、2行的函数重载起作用。 用这两种以外的其他类型的论点来称呼学校是一个错误。
(3) 第1行和第2行定义了两个函数,第3行只是这两个重载函数的实现。
我在知乎上看到的总结是这么说的,因为很多人认为typescript重载的存在意义不大:
TypeScript的重载是给调用者看的,让调用者知道如何调用(同时tsc也会进行静态检测,防止错误调用)。
IDE 的有用代码提示以及返回值的类型约束。
发表评论