1. 程式人生 > >JavaScript中的函式過載(Function overloading)

JavaScript中的函式過載(Function overloading)

說明

JavaScript 中沒有真正意義上的函式過載。

函式過載

函式名相同,函式的引數列表不同(包括引數個數和引數型別),根據引數的不同去執行不同的操作。

我們舉個例子看看

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 中模擬實現過載的效果。

arguments 物件,是函式內部的一個類陣列物件,它裡面儲存著呼叫函式時,傳遞給函式的所有引數。

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

這個例子非常簡單,就是通過判斷 arguments 物件的 length 屬性來確定有幾個引數,然後執行什麼操作。

但是引數少的情況下,還好,如果引數多一些,if 判斷就需要寫好多,就麻煩了。

所以,我們再來看一個經典的例子
在看這個例子之前,我們先來看一個需求,我們有一個 users 物件,users 物件的values 屬性中存著一些名字。
一個名字由兩部分組成,空格左邊的是 first-name ,空格右邊的是 last-name,像下面這樣。

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

我們要在 users 物件 中新增一個 find 方法,

當不傳任何引數時, 返回整個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( ) 方法依賴於三個方法:

1、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( ) 方法判斷第一個引數是字串還是物件的。

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

在css( )方法中,如果有傳第二個引數,也就是有要設定的屬性值時,那就會呼叫 jQuery.style( ) 方法設定樣式

3、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函式過載Function overloading

說明 JavaScript 中沒有真正意義上的函式過載。 函式過載 函式名相同,函式的引數列表不同(包括引數個數和引數型別),根據引數的不同去執行不同的操作。 我們舉個例子看看 function overload(a){ conso

JavaScript的方法Function型別

Function 函式是 ES 中非常重要的一部分,函式實際上也是物件。 每個函式都是 Function 型別的例項,而且都與其他引用型別一樣具有屬性和方法。 由於函式是物件,因此函式名實際上也是一個指向函式物件的指標,不會跟某個函式繫結。 定義函式 函式定義有三種方式:

javascript自執行自呼叫函式的兩種寫法

自執行函式定義: 自執行函式或是自呼叫函式 宣告完了,馬上進行呼叫,只能使用一次,,有兩種寫法,舉個栗子如下: 寫法一: 格式:(函式)(實參) <script&g

avaJFrame類函式addWindowListenernew WindowAdapter

在java編寫的過程中常常遇到樣的一段程式碼 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) {

函式型別Function Types函式型別和其他型別一樣

函式型別(Function Types) 每個函式都有種特定的函式型別,由函式的引數型別和返回型別組成。 例如: 這個例子中定義了兩個簡單的數學函式:addTwoInts 和 multiplyTwoInts。這兩個函式都傳入兩個 Int型別, 返回一個合適的I

一、函式模板Function Template

本系列是《C++Template》(作者:David Vandevoorde, Nicolai M. Josuttis)的學習總結。 1:函式模板(Function Template) 所謂函式模板是由引數表示的一系列的函式。函式模板可以被不同的型別引數

C++的函式過載具體應用

一、C++過載函式的函式匹配步驟 1先根據函式名進行查詢--------找到一個或多個同名的函式(若一個都沒有,報編譯錯誤) 2找到函式名後按照“型別”+“個數”進行匹配----------若找到一個完全匹配(引數個數和型別都相同)的函式,則呼叫該函式    

Kotlin 其他 --- 操作符過載Operator overloading

1、操作符過載 Kotlin 允許我們為自己的型別提供預定義的一組操作符的實現。這些操作符具有固定的符號表示 (如 + 或 *)和固定的優先順序。為實現這樣的操作符,我們為相應的型別(即二元操作符左側的型別和一元操作符的引數型別)提供了一個固定名字的 成員函式

C++11系列——函式物件Function Object

之前總結過一篇Boost之高階函式——函式物件,介紹了幾個用於處理函式物件的 Boost C++ 庫。而目前C++11的標準庫std已經提供了函式物件的一些功能。 In mathematics and computer science, a high

C七:指向函式的指標 ------ 函式指標function pointer

   函式具有可賦值給指標的實體記憶體地址,一個函式的函式名就是一個指標,它指向函式的程式碼。一個函式的地址是該函式的進入點,也是呼叫函式的地址。函式的呼叫可以通過函式名,也可以通過指向函式的指標來呼叫。函式指標還允許將函式作為變元傳遞給其他函式。       不帶括號和變

C++之函式物件Function Object & for_each

函式物件 對於一些用到函式作為引數的c++STL演算法(如下面的for_each演算法函式),函式的傳遞當然可以用泛化的函式指標來進行,但是c++STL常使用的是函式物件,目的在於更簡潔、不依賴於當前

詳解JavaScript的replace函式

  Javascript中字串物件有一個方法replace(),它的作用非常強大。這裡把它的用法整理一下。   一、方法簡介   該方法的簽名是:replace([RegExp|String],[String|Function])。   該方法 返回一個新的字串,但並不改變字串本身。

<C++學習二十>C++函式過載的理解未完待續

摘要: 本篇部落格僅作為筆記,如有侵權,請聯絡,立即刪除(網上找部落格學習,然後手記筆記,因紙質筆記不便儲存,所以儲存到網路筆記)。   我們平時寫程式碼中會用到幾個函式但是他們的實現功能相同,但是有些細節卻不同。例如:交換兩個數的其中包括(int,float,char,double)這些型別。這C語言中我

A1—淺談JavaScript的原型

js原型是什麽?想要了解這個問題,我們就必須要知道對象。對象根據w3cschool上的介紹:對象只是帶有屬性和方法的特殊數據類型。我們知道,數組是用來描述數據的。其實呢,對象也是用來描述數據的。只不過有一點點的區別,那就是數組的下標只能是數字。所以,數組最好只用來裝同樣意義的內容。比如說[1,2,3,4,5]

詳解 javascript 的比較==和===

不一致 mit 如果 asc onu tin 算法 復雜 undefine 抽象相等比較算法 比較運算 x==y, 其中 x 和 y 是值,產生 true 或者 false。這樣的比較按如下方式進行: 若 Type(x) 與 Type(y) 相同, 則 若 Type(x)

函式過載overload

1、什麼是過載 方法名相同;但引數列表不同;稱為函式過載 在同一個類中,允許存在一個以上的同名函式,只要他們的引數個數或者引數的型別不同即可 2、過載的呼叫 根據你傳入的引數列表呼叫函式,即你傳入兩個引數函式則呼叫兩個引數的 函式;如 public

請編寫一個函式,使其可以刪除某個連結串列給定的非末尾節點,你將只被給定要求被刪除的節點

今天給大家分享一個小題目,如下: 請編寫一個函式,使其可以刪除某個連結串列中給定的(非末尾)節點,你將只被給定要求被刪除的節點。 現有一個連結串列 -- head = [4,5,1,9],它可以表示為: 4 -> 5 -> 1 -> 9

MySQL函式field的用法

MySQL中的field()函式,可以用來對SQL中查詢結果集進行指定順序排序。 str與str1,str2,str3,str4比較,其中str指的是欄位名字 意為:欄位str按照字串str1,str2,str3,str4的順序返回查詢到的結果集。如果表中str欄位值不存在於str

c++cmath函式學生筆記

c++中cmath是一個重要的數學函式庫。呼叫時必須包含cmath主檔案。 #include #include 其常用函式原型: int abs(int n) 求n的絕對值 double cos/sin/tan(double x) 求x的三角函式值(x為弧度值) double exp(dou

C++拷貝建構函式和運算子過載VC實現

String.h檔案: #ifndef STRING_H #define STRING_H #include <ostream> using namespace std; class String { public: String(){ m_pStr = nullptr