所謂重載,就是一組相同的函數名,有不同個數的參數,在使用時調用一個函數名,傳入不同參數,根據你的參數個數,來決定使用不同的函數!但是我們知道js中是沒有重載的,因為后定義的函數會覆蓋前面的同名函數,但是我們又想實現函數重載該怎么辦呢?
第一種方法:
這種方法比較簡單,給一個思路,大家肯定都能理解,就是函數內部用switch語句,根據傳入參數的個數調用不同的case語句,從而功能上達到重載的效果。
這種方法簡單粗暴。但是對於一個正在學習js的人來說,這種方法未免太敷衍了。
下面重點介紹一下第二種,老實說我看的時候很吃力看了一個小時才捋清楚,因為有的知識點雖然看過了但是不熟悉。這次就給我上了一課,教會了我很多東西。
第二種方法:
我們這個例子,是如果你不傳入參數,就會輸出所有的人,輸入firstname,就會輸出匹配的人,如果輸入全名的人,也會輸出匹配的人。如果用重載的話,用戶體驗確實會很好(這個例子是我學習時從網上扒下來的,很有代表性,但是他們都沒有寫實現過程,我來和大家談論一下實在的東西)
function method(obj,name,fnc){ var old = obj[name]; console.log(old instanceof Function); obj[name] = function(){ console.log(arguments.length+" "+fnc.length); if(arguments.length === fnc.length){ return fnc.apply(this,arguments); }else if(typeof old === "function"){ return old.apply(this,arguments); } } } var people = { values:["Zhang san","Li si"] }; method(people,"find",function(){ console.log("無參數"); return this.values; }) method(people,"find",function(firstname){ console.log("一個參數"); 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; }) method(people,"find",function(firstname,lastname){ console.log("兩個參數"); 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()); console.log(people.find("Zhang"));
思路:這段代碼第一眼看到我是懵逼的,再看有點思路,再看又懵了。這種方法巧妙的運用了閉包原理,既然js后面的函數會覆蓋前面的同名函數,我就強行讓所有的函數都留在內存里,等我需要的時候再去找它。有了這個想法,是不是就想到了閉包,函數外訪問函數內的變量,從而使函數留在內存中不被刪除。就是閉包。
實現過程:我們看一下上面這段代碼,最重要的是method方法的定義:這個方法中最重要的一點就是這個old,這個old真的很巧妙。它的作用相當於一個指針,指向上一次被調用的method函數,這樣說可能有點不太懂,我們根據代碼來說,js的解析順序從上到下為。
1.解析method(先不管里面的東西)
2.method(people,"find",function() 執行這句的時候,它就回去執行上面定義的方法,然后此時old的值為空,因為你還沒有定義過這個函數,所以它此時是undefined,然后繼續執行,這是我們才定義 obj[name] = function(),然后js解析的時候發現返回了fnc函數,更重要的是fnc函數里面還調用了method里面的變量,這不就是閉包了,因為fnc函數的實現是在調用時候才會去實現,所以js就想,這我執行完也不能刪除啊,要不外面那個用啥,就留着吧先(此處用call函數改變了fnc函數內部的this指向)
3.好了第一次method的使用結束了,開始了第二句,method(people,"find",function(firstname) 然后這次使用的時候,又要執行old = obj[name]此時的old是什么,是函數了,因為上一條語句定義過了,而且沒有刪除,那我這次的old實際上指向的是上次定義的方法,它起的作用好像一個指針,指向了上一次定義的 obj[name]。然后繼續往下解析,又是閉包,還得留着。
4.第三此的method調用開始了,同理old指向的是上次定義的 obj[name] 同樣也還是閉包,還得留着。
5.到這里,內存中實際上有三個 obj[name],因為三次method的內存都沒有刪除,這是不是實現了三個函數共存,同時還可以用old將它們聯系起來是不是很巧妙
6.我們 people.find() 的時候,就會最先調用最后一次調用method時定義的function,如果參數個數相同 也就是 arguments.length === fnc.length 那么就執行就好了,也不用找別的函數了,如果不相同的話,那就得用到old了 return old.apply(this,arguments); old指向的是上次method調用時定義的函數,所以我們就去上一次的找,如果找到了,繼續執行 arguments.length === fnc.length 如果找不到,再次調用old 繼續向上找,只要你定義過,肯定能找到的,對吧!
總結:運用閉包的原理使三個函數共存於內存中,old相當於一個指針,指向上一次定義的function,每次調用的時候,決定是否需要尋找。
執行過程很容易說明這一點:首先第一次調用的時候 old肯定不是函數,所以instance判斷是false,繼續調用的話就會為true。然后,我們調用method的順序,是從沒有參數到兩個參數,所以我們最先調用find方法,是最后一次method調用時定義的,所以fnc的length長度是2.然后向上找,length為1,最后終於找到了length為0的然后執行,輸出。
原創學習博客,歡迎討論,指出不足之處。