JavaScript實現函數重載

參考:淺談JavaScript函數重載

簡單的說如果JS函數需要實現重載的話,可以根據arguments對象的length值進行判斷。

function overLoading() {
  // 根據arguments.length,對不同的值進行不同的操作
  switch(arguments.length) {
    case 0:
      /*操作1的代碼寫在這裏*/
      break;
    case 1:
      /*操作2的代碼寫在這裏*/
      break;
    case 2:
      /*操作3的代碼寫在這裏*/
       
  //後面還有很多的case......
}}

更好的方法是在JQuery之父John Resig寫的《secrets of the JavaScript ninja》找到了一個絕佳巧妙的方法!那種方法充分的利用了閉包的特性!

我們現在有這樣的一個需求,有一個people對象,裏面存着一些人名,如下:

var people = {
  values: ["Dean Edwards", "Sam Stephenson", "Alex Russell", "Dean Tom"]
};
我們希望people對象擁有一個find方法,當不傳任何參數時,就會把people.values裏面的所有元素返回來;當傳一個參數時,就把first-name跟這個參數匹配的元素返回來;當傳兩個參數時,則把first-name和last-name都匹配的才返回來。因爲find方法是根據參數的個數不同而執行不同的操作的,所以,我們希望有一個addMethod方法,能夠如下的爲people添加find的重載:
addMethod(people, "find", function() {}); /*不傳參*/
addMethod(people, "find", function(a) {}); /*傳一個*/
addMethod(people, "find", function(a, b) {}); /*傳兩個*/
這時候問題來了,這個全局的addMethod方法該怎麼實現呢?John Resig的實現方法如下,代碼不長,但是非常的巧妙:
function addMethod(object, name, fn) {
  var old = object[name]; //把前一次添加的方法存在一個臨時變量old裏面
  object[name] = function() { // 重寫了object[name]的方法
    // 如果調用object[name]方法時,傳入的參數個數跟預期的一致,則直接調用
    if(fn.length === arguments.length) {
      return fn.apply(this, arguments);
    // 否則,判斷old是否是函數,如果是,就調用old
    } else if(typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}
現在,我們一起來分析一個這個addMethod函數,它接收3個參數,第一個爲要綁定方法的對象,第二個爲綁定的方法名稱,第三個爲需要綁定的方法(一個匿名函數)。函數體的的分析已經在註釋裏面了。 

OK,現在這個addMethod方法已經實現了,我們接下來就實現people.find的重載啦!全部代碼如下:
//addMethod
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 people = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};

/* 下面開始通過addMethod來實現對people.find方法的重載 */

// 不傳參數時,返回peopld.values裏面的所有元素
addMethod(people, "find", function() {
  return this.values;
});

// 傳一個參數時,按first-name的匹配進行返回
addMethod(people, "find", function(firstName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i].indexOf(firstName) === 0) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 傳兩個參數時,返回first-name和last-name都匹配的元素
addMethod(people, "find", function(firstName, lastName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i] === (firstName + " " + lastName)) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 測試:
console.log(people.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean Edwards")); //["Dean Edwards"]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章