TypeScript學習6-函數

引言

TypeScript中的函數和JavaScript中的函數,和其餘特性相比,是相差不大的。
這裏會補充一點進階知識。typescript

函數相關的知識點

下面列舉一些TypeScript中函數相關的知識點。數組

類型聲明

TypeScript中的函數定義,須要聲明參數和返回值的類型。閉包

// function add
function add(x: number, y: number): number {
    return x + y;
}
// arrow funtion add
const add = (x: number, y: number) => x + y;

上面是兩種基本用法:普通函數定義、箭頭函數定義。
其中隱藏有一個點:類型推斷,箭頭函數沒有聲明返回值類型,這裏編譯器不會報錯,由於它能夠推斷出返回值類型。這個特性,在數據類型學習的時候也接觸過,規則也是相似的:能推斷出類型的,不須要顯示聲明框架

可選參數和參數默認值

這兩個特性實際使用的時候是頗有用的,很清晰,也省掉很多代碼。dom

// function log
function myLog(message: string,  level: ('info' | 'warn' | 'error') = 'info', prefix?: string): void {
  // todo, logging
}

這裏:函數

  • level參數是有一個默認值的
  • prefix參數是可選的

注意一點:可選參數必須放最後post

剩餘參數

剩餘參數就是,剩餘的其餘參數,這裏用到告終構,看示例。學習

function myCallback(status: boolean, ...restData: any[]) {
  console.log(`${status}: ${restData.join()}`);
}

myCallback(false);
myCallback(true, '完成一個');
myCallback(true, '完成第一個', '完成第二個');

剩餘參數使用起來很是靈活。this

進階知識點

this指向

JavaScript中的this是個入門的難點,JavaScript函數的this是和調用上下文直接相關的,和定義不相關,因此很容易出現this指向和預期不一致的問題。 url

目前有幾種解決方案:this綁定、箭頭函數、閉包。

箭頭函數

箭頭函數的出現,能夠緩解了這個難題。箭頭函數中的this,是和定義的上下文相關(底層是利用閉包實現的),以下例:

const myLottery = {
  prizes: ['pen', 'pencil', 'notebook', 'dictionary'],
  getDraw: function() {
    return (name: string) => {
      const result = Math.floor(Math.random() * 4);
      return `Bingo, ${name}, you win the ${this.prizes[result]}!`;
    }
  }
}

const myDraw = myLottery.getDraw();
console.log(myDraw('Tom'));

閉包實現

閉包方案就是在函數上下文中把this保存在變量中。

...
  getDraw: function() {
    const _this = this;
    return function (name: string) {
      const result = Math.floor(Math.random() * 4);
      return `Bingo, ${name}, you win the ${_this.prizes[result]}!`;
    }
  }
  ...

bind綁定

bind綁定在TypeScript裏面會麻煩一點,須要聲明this類型,不然編譯不過,提示this類型不肯定。

interface Lottery {
  prizes: string[];
  getDraw(): {(name: string): string};
}

const myLottery: Lottery = {
  prizes: ['pen', 'pencil', 'notebook', 'dictionary'],
  getDraw: function() {
    return function (this: Lottery, name: string) {
      const result = Math.floor(Math.random() * 4);
      return `Bingo, ${name}, you win the ${this.prizes[result]}!`;
    }.bind(this);
  }
}

const myDraw = myLottery.getDraw();
console.log(myDraw('Tom'));

函數重載

TypeScript的重載,我是感受有點牽強的,以下示例:

function myAdd(x: number, y: number): number;
function myAdd(x: string, y: string): string;
function myAdd(x, y): any {
  if (typeof x === 'number' && typeof y === 'number') {
    return x + y;
  } else if (typeof x === 'string' && typeof y === 'string') {
    return x + y;
  }
}

myAdd(1, 2);
myAdd('Hello', 'world');

這種實現,其實就是動態參數的分支處理,好處是代碼提示比較友好,能識別出來重載。

沒法實現相似靜態語言的重載的緣由以前也提過:靜態語言,如Java,函數重載,是生成了多個不一樣名的函數,同時使用的地方也會被更新成對應的函數名。TypeScript要兼容JavaScript,這個就沒法實現。

高階函數

高階函數聽起來很高大上,其實概念並不難:參數是函數或返回值是函數的函數

咱們接觸比較多的好比,數組內置的map方法。

// 數組元素自增
let arr  = [1, 2, 3];

// 傳統方式
for(let i=0; i < arr.length; i++) {
    arr[i]++;
}

// map
arr = arr.map(function(item) {
    return item + 1;
});

// 結合箭頭函數
arr = arr.map(item => item + 1);

下面再學一個複雜一點的案例,前置處理器。好比,咱們要在請求數據以前,記錄一些日誌。

interface Request {
  (url: string, options: {[key: string]: any}): Promise<any>;
}

function withLog(func: Request): Request {
  return function(url: string, options: {[key: string]: any}) {
    console.log('request');
    return func(url, options);
  };
}

function myRequest(url: string, options: {[key: string]: any}) {
  return new Promise(function(resolve) {
      setTimeout(() => {
        resolve('completed!')   
      }, 1000);
  });
}

withLog(myRequest)('/a', {method: 'post'}).then(val => {
  console.log(val);
})

上述方案就是把請求函數經過另一個函數包裝成一個新的函數。

再複雜一點,像這樣,參數是函數,返回值也是函數的,是能夠無限嵌套的,每嵌套一次,它就多了一個功能。這在不少框架裏面特別常見,用於抽象和解耦。

function withLog2(func: Request): Request {
  return function(url: string, options: {[key: string]: any}) {
    return func(url, options).then(val => {
      return `[${val}]`;
    });
  };
}

withLog2(withLog(myRequest))('/a', {method: 'post'}).then(val => {
  console.log(val);
})
相關文章
相關標籤/搜索