JavaScript中的函數重載(Function overloading)

說明

JavaScript 中沒有真正意義上的函數重載。javascript

函數重載

函數名相同,函數的參數列表不一樣(包括參數個數和參數類型),根據參數的不一樣去執行不一樣的操做。 css

咱們舉個例子看看html

function overload(a){
    console.log('一個參數')
}

function overload(a,b){
    console.log('兩個參數')
}

// 在支持重載的編程語言中,好比 java
overload(1);         //一個參數
overload(1,2);    //兩個參數


// 在 JavaScript 中
overload(1);         //兩個參數
overload(1,2);    //兩個參數

在JavaScript中,同一個做用域,出現兩個名字同樣的函數,後面的會覆蓋前面的,因此 JavaScript 沒有真正意義的重載。前端

可是有各類辦法,能在 JavaScript 中模擬實現重載的效果。java

先看第一種辦法,經過 arguments 對象來實現

arguments 對象,是函數內部的一個類數組對象,它裏面保存着調用函數時,傳遞給函數的全部參數。jquery

function overload () {
  if (arguments.length === 1) {
    console.log('一個參數')
  }
  if (arguments.length === 2) {
    console.log('兩個參數')
  }
}

overload(1);      //一個參數
overload(1, 2);  //兩個參數

這個例子很是簡單,就是經過判斷 arguments 對象的 length 屬性來肯定有幾個參數,而後執行什麼操做。git

可是參數少的狀況下,還好,若是參數多一些,if 判斷就須要寫好多,就麻煩了。github

因此,咱們再來看一個經典的例子
在看這個例子以前,咱們先來看一個需求,咱們有一個 users 對象,users 對象的values 屬性中存着一些名字。
一個名字由兩部分組成,空格左邊的是 first-name ,空格右邊的是 last-name,像下面這樣。編程

var users = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};

咱們要在 users 對象 中添加一個 find 方法,segmentfault

當不傳任何參數時, 返回整個users .values
當傳一個參數時,就把 first-name 跟這個參數匹配的元素返回;
當傳兩個參數時,則把 first-name 和 last-name 都匹配的返回。

這個需求中 find方法 須要根據參數的個數不一樣而執行不一樣的操做,下來咱們經過一個 addMethod 函數,來在 users 對象中添加這個 find 方法。

function addMethod (object, name, fn) {
  // 先把原來的object[name] 方法,保存在old中
  var old = object[name];

  // 從新定義 object[name] 方法
  object[name] = function () {
    // 若是函數須要的參數 和 實際傳入的參數 的個數相同,就直接調用fn
    if (fn.length === arguments.length) {
      return fn.apply(this, arguments);

      // 若是不相同,判斷old 是否是函數,
      // 若是是就調用old,也就是剛纔保存的 object[name] 方法
    } else if (typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}

addMethod 函數,它接收3個參數
第一個:要綁定方法的對象,
第二個:綁定的方法名稱,
第三個:須要綁定的方法

這個 addMethod 函數在判斷參數個數的時候,除了用 arguments 對象,還用了函數的 length 屬性。

函數的 length 屬性,返回的是函數定義時形參的個數。

簡單說 函數的 length 是,函數須要幾個參數,而 arguments.length 是調用函數時,真的給了函數幾個參數

function fn (a, b) {
  console.log(arguments.length)
}
console.log(fn.length);  // 2
fn('a');    // 1

下來咱們來使用這個 addMethod 函數

// 不傳參數時,返回整個values數組
function find0 () {
  return this.values;
}
// 傳一個參數時,返回firstName匹配的數組元素
function find1 (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;
}
// 傳兩個參數時,返回firstName和lastName都匹配的數組元素
function find2 (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;
}
// 給 users 對象添加處理 沒有參數 的方法
addMethod(users, "find", find0);

// 給 users 對象添加處理 一個參數 的方法
addMethod(users, "find", find1);

// 給 users 對象添加處理 兩個參數 的方法
addMethod(users, "find", find2);

// 測試:
console.log(users.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(users.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(users.find("Dean","Edwards")); //["Dean Edwards"]

addMethod 函數是利用了閉包的特性,經過變量 old 將每一個函數鏈接了起來,讓全部的函數都留在內存中。

每調用一次 addMethod 函數,就會產生一個 old,造成一個閉包。
咱們能夠經過 console.dir(users.find) ,把 find 方法打印到控制檯看看。

圖片描述

上面這個例子是 jQuery 之父 John Resig 寫的,他在他的博客和他寫的書 《secrets of the JavaScript ninja》初版中都有提到過,在書中的第4章中也有講解 Function overloading,文中的 addMethod 函數 就是書中的例子 4.15,感興趣的朋友能夠去看看。

上面的例子,本質都是在判斷參數的個數,根據不一樣的個數,執行不一樣的操做,而下來舉的例子是經過判斷參數的類型,來執行不一樣的操做。

咱們看看 jQuery 中的 css( ) 方法

css( ) 方法返回或設置匹配的元素的一個或多個樣式屬性。

css(name|pro|[,val|fn])

圖片描述

咱們能夠看到 css( ) 方法,有5種 參數狀況,其中3種是一個參數,另外兩種是兩個參數。
而在只有一個參數的狀況下,若是參數類型是字符串或者數組就是獲取屬性值,而若是參數是對象,就是是設置屬性值。

jQuery 的 css( ) 方法就是經過判斷參數的類型,來肯定執行什麼操做。

咱們來看看jQuery 3.3.1中的源碼

// name 表示屬性名
// value 表示屬性值
css: function( name, value ) {
    return access( this, function( elem, name, value ) {
        var styles, len,
            map = {},
            i = 0;

        // 判斷屬性名是否是數組
        // 是數組就遍歷,調用jQuery.css 方法傳入每一個屬性名,獲取樣式
        if ( Array.isArray( name ) ) {
            styles = getStyles( elem );
            len = name.length;

            for ( ; i < len; i++ ) {
                map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
            }

            return map;
        }

        // 若是value 不等於 undefined 就調用jQuery.style 方法設置樣式
        // 若是value 等於 undefined 就調用jQuery.css 方法獲取樣式
        return value !== undefined ?
            jQuery.style( elem, name, value ) :
            jQuery.css( elem, name );
    }, name, value, arguments.length > 1 );
}

css( ) 方法依賴於三個方法:

一、jQuery.access( ) 方法,這個方法能夠獲取 或 設置,一個或者多個屬性值

jQuery.access( ) 方法裏有這樣的代碼

// 設置多個屬性值
// 若是屬性名(key)的類型是 object,就遍歷這個對象
// 遍歷一次就調用一次 access()方法,並傳入此次的屬性名和屬性值
if ( jQuery.type( key ) === "object" ) {
    chainable = true;
    for ( i in key ) {
        jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
    }

// 設置一個值
} else if ( value !== undefined ) {
    ......
}

也就是這個方法,在幫 css( ) 方法判斷第一個參數是字符串仍是對象的。

二、jQuery.style( ) 方法:在DOM節點上讀取或設置樣式屬性

在css( )方法中,若是有傳第二個參數,也就是有要設置的屬性值時,那就會調用 jQuery.style( ) 方法設置樣式

三、jQuery.css( ) :在DOM元素上讀取DOM樣式值

這裏的 jQuery.css( ) 是經過 jQuery.extend( ) 添加的方法,而咱們最開始提到的 css( )方法,是經過 jQuery.fn.extend( ) 添加的方法,他們不是同一個方法。

jQuery.extend( )與 jQuery.fn.extend( )的區別

jQuery.extend( )是爲jQuery類添加類方法(靜態方法),須要經過jQuery類來調用(直接使用 $.xxx 調用);

jQuery.fn.extend( )是爲jQuery類添加成員數(實例方法),全部jQuery實例均可以直接調用(須要使用 $().xxx 調用)。

重載的好處

重載實際上是把多個功能相近的函數合併爲一個函數,重複利用了函數名。
假如jQuery中的css( )方法不使用 重載,那麼就要有5個不一樣的函數,來完成功能,那咱們就須要記住5個不一樣的函數名,和各個函數相對應的參數的個數和類型,顯然就麻煩多了。

總結

雖然 JavaScript 並無真正意義上的重載,可是重載的效果在JavaScript中卻很是常見,好比 數組的 splice( )方法,一個參數能夠刪除,兩個參數能夠刪除一部分,三個參數能夠刪除完了,再添加新元素。
再好比 parseInt( )方法 ,傳入一個參數,就判斷是用十六進制解析,仍是用十進制解析,若是傳入兩個參數,就用第二個參數做爲數字的基數,來進行解析。

文中提到的實現重載效果的方法,本質都是對參數進行判斷,無論是判斷參數個數,仍是判斷參數類型,都是根據參數的不一樣,來決定執行什麼操做的。

雖然,重載能爲咱們帶來許多的便利,可是也不能濫用,不要把一些根本不相關的函數合爲一個函數,那樣並無什麼意義。

參考

淺談JavaScript函數重載

js如何實現重載

JavaScript函數重載

前端簡單說

相關文章
相關標籤/搜索