今兒個甚是乏累呢~
但是
<----------------------------------------------------------------------下面可能是正題兒---------------------------------------------------------------------->
1.函數聲明
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歲
這是可以通過編譯的,不過事實上,上面的代碼只對等號右側的匿名函數進行了類型定義,而等號左邊的 student
,是通過賦值操作進行類型推論而推斷出來的。
如果需要我們手動給 student
添加類型,則應該是這樣:
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 }
據說這叫“按上下文歸類”,是類型推論的一種。
(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.接口中函數的定義
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.可選參數
之前提到過函數的參數是不可多不可少的,但是也可以像接口那樣的形式用?表示可選參數
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.默認參數
在typescript中,可以為函數中的參數設置一個默認值,當用戶沒有傳這個參數或者傳的值是undefined時,就顯示默認值。
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中,你可以使用arguments來訪問所有傳入的參數。
在typescript里,你可以把所有參數收集到一個變量里:
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表達式和使用this
在JavaScript里,this
的值在函數被調用的時候才會指定。學過JavaScript的都知道this很是讓人頭疼啊!
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,因為這里沒有對this進行動態綁定
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行打印出了
即student這個對象,大功告成
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
上面函數的參數和返回值的類型均使用了聯合類型的形式,這樣一來我們就不能准確的表達我傳入的是number類型的返回的就要是number類型得了
這時就要引入重載的概念了
在學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 會優先從最前面的函數定義開始匹配,所以多個函數定義如果有包含關系,需要優先把精確的定義寫在前面。
再給個栗子:
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行的函數重載起作用。用除這兩種外的其他類型的參數調用schools會產生錯誤。
(3)1,2行是定義了兩個函數,而3行只是對這兩個重載函數的實現。
我在知乎上看到的總結是這樣說的,因為會有好多人覺得typescript的重載存在的意義不大:
TypeScript 的重載是為了給調用者看,方便調用者知道該怎么調用(同時 tsc 也會進行靜態檢查以避免錯誤的調用)。
有助於IDE的代碼提示,以及返回值的類型約束。
<----------------------------------------------------------------------上面可能是正題兒---------------------------------------------------------------------->