JS 利用引數個數進行函式過載

任乃千發表於2017-04-22

分享下《JavaScript忍者祕籍》中的一種函式過載方式

首先說一下函式的length屬性:

所有的函式都有一個經常被忽略的屬性,但卻可以讓我們瞭解函式的宣告,那就是length屬性。和arguments引數的length屬性不同的是,它表示函式宣告時所需傳入的形引數量。

function makeNinja(name){};
//makeNinja.length === 1;複製程式碼

對一個函式,在引數方面,我們可以確定兩件事情:

  • 通過其length屬性,可以知道宣告瞭多少命名引數
  • 通過arguments.length,可以知道在呼叫時傳入了多少引數

過載函式的方法:

/*
* object為要繫結方法的物件
* name為繫結方法所用的屬性名稱
* fn為要繫結的方法
* */
function addMethod(object, name, fn) {
  //儲存原有的函式,因為呼叫的時候可能不匹配傳入的引數個數
  var old = object[name];
  //建立一個新匿名函式作為新方法
  object[name] = function () {
    //如果該匿名函式的形參個數和實參個數匹配,就呼叫該函式
    if (fn.length === arguments.length) {
      return fn.apply(this, arguments)
    //如果傳入的引數不匹配,則呼叫原有的引數
    } else if (typeof old === 'function') {
      return old.apply(this, arguments);
    }
  }
}複製程式碼

用法

var ninja = {};
addMethod(ninja, 'whatever', function () {
  /* do something */
});
addMethod(ninja, 'whatever', function (a) {
  /* do something else */
});
addMethod(ninja, 'whatever', function (a, b) {
  /* yet something else */
});複製程式碼

分析

addMethod()第一次呼叫將建立一個新匿名函式,傳入零個引數進行呼叫的時候將會呼叫該fn函式。由於此時ninja是一個新物件,所以這時候不用擔心之前建立的方法。

addMethod()第二次呼叫的時候,首先將之前的同名函式儲存到一個變數old中,然後將新建立的匿名函式作為方法。新方法首先檢查傳入的函式個數是否為一,如果是,就呼叫剛才傳入的fn函式;如果不是,則重新呼叫儲存在old上的函式,重新呼叫該函式時,將會再次檢查引數個數是否為零,繼而呼叫引數個數為零的fn版本的函式。

addMethod()第三次呼叫的時候,傳入了一個接收兩個引數的fn函式,然後判斷邏輯相同:建立一個匿名函式作為方法,判斷是否傳入引數的個數為兩個,如果是則呼叫兩個引數的fn函式,否則繼續判斷引數個數是否為一。

這種方法就像剝洋蔥一樣,每一層都檢查引數個數是否匹配,如果不匹配的話,就進入上一層建立的函式。內部函式是通過閉包訪問到old和fn的。

這是一個絕佳的技巧,因為這些函式實際上並沒有儲存於任何典型的資料結構中,而是在閉包裡作為引用進行儲存。應該注意的是,在使用這個特定的技巧時,需要注意以下兩點:

  • 這個過載只適用於不同數量的引數,但並不區分型別、引數名或其他東西。這些才是我們經常想做的事情
  • 這個過載方法會有一些函式呼叫的開銷

相關文章