引言
最近在js的學習中,看到了函式過載的問題,一開始,只看到了實現程式碼,看著程式碼冥思苦想了半個小時,總算是理清了其實現的原理,也為其實現的巧妙感到讚歎,也是在自己搞懂原理之後,去網路上搜尋了下,才知道,這個實現方法是jQuery作者John Resig在《JavaScript忍者祕籍》中對函式過載的實現,設計十分的巧妙,寫下此文,給大家做一個分享
什麼是函式的過載
過載,簡單說,就是函式或者方法有相同的名稱,但是引數列表不相同的情形,這樣的同名不同引數的函式或者方法之間,互相稱之為過載函式或者方法。
先來看看效果
function addMethod (obj, name, fn) {
var old = obj[name];
obj[name] = function () {
if (fn.length === arguments.length) {
return fn.apply(this, arguments)
} else if (typeof old === 'function') {
return old.apply(this, arguments)
}
}
}
var person = {userName: 'bear鮑的小小熊'}
addMethod(person, 'show', function () {
console.log(this.userName + '---->' + 'show1')
})
addMethod(person, 'show', function (str) {
console.log(this.userName + '---->' + str)
})
addMethod(person, 'show', function (a, b) {
console.log(this.userName + '---->' + (a + b))
})
person.show()
person.show('bkl')
person.show(10, 20)
複製程式碼
輸出的結果
//bear鮑的小小熊---->show1
//bear鮑的小小熊---->bkl
//bear鮑的小小熊---->30
複製程式碼
我們給一個物件新增了一個show方法,這個show方法,每次傳入的引數不一樣,它進行的處理也是不一樣的
為了後文更好理解,這裡先對fn.length這個大家可能陌生的屬性做個解釋,先看下面的程式碼
function fn(a,b,c) {}
fn.length // 3
function fn(a,b,c,d) {}
fn.length // 4
複製程式碼
fn.legnth,是函式fn在定義時,形參的個數.好了,讓我們繼續往下講吧
這個addMethod函式,簡單的來說,就是給一個物件新增一個指定name的方法fn(後文中為了方便大家理解,我們就以這個例子中的show來指代這個name吧),他利用了閉包,通過變數old,將每次傳進來的fn給儲存起來,我們每次呼叫這個show方法,根據傳入的引數的不同,我們的程式碼可能多次通過old來找到之前傳入的fn函式
下面我們來對這個方法進行解析為了看的更加直觀,我們對之前的addMethod的函式做一點小小的改造,其實就是加入了一個console.log(),可以方便我們理解,函式的執行過程
function addMethod (obj, name, fn) {
var old = obj[name];
obj[name] = function () {
console.log(1) //列印1
if(fn.length === arguments.length){
console.log(2) // 列印2
return fn.apply(this,arguments);
}else if(typeof old === 'function'){
console.log(3) // 列印3
return old.apply(this,arguments);
}
}
}
addMethod(person, 'show', function () {
console.log(this.userName + '---->' + 'show1')
})
addMethod(person, 'show', function (str) {
console.log(this.userName + '---->' + str)
})
addMethod(person, 'show', function (a, b) {
console.log(this.userName + '---->' + (a + b))
})
複製程式碼
下面,我們看一下person.show方法,在不傳參,傳了一個引數,與傳了兩個引數時,函式執行的具體過程,
- person.show(10, 20)
1
2
bear鮑的小小熊---->30
複製程式碼
可見,傳入兩個引數的時候,只列印了一個1,一個2,就將對應的執行函式執行了.其實這個時候person.show函式的作用域內 fn為下面這個函式
function (a, b) {
console.log(this.userName + '---->' + (a + b))
}
複製程式碼
- person.show('bkl')
1
3
1
2
bear鮑的小小熊---->bkl
複製程式碼
傳入一個引數的時候執行結果為 1 --> 3 --> 1 --> 2 --> 處理後的結果 在這個過程中由於執行person.show方法時,fn.length === 2,而我們傳入的引數為1個,那麼函式會執行到
return old.apply(this,arguments);
複製程式碼
這個時候的old是什麼呢?這個時候的old其實是在下面這個函式執行之前的person.show方法
addMethod(person, 'show', function (a, b) {
console.log(this.userName + '---->' + (a + b))
})
複製程式碼
執行之後person.show函式作用域內的fn函式也就是下面這個方法
function (str) {
console.log(this.userName + '---->' + str)
}
複製程式碼
- person.show()
1
3
1
3
1
2
bear鮑的小小熊---->show1
複製程式碼
上面的也是同理,根據這個輸出結果,不難看出,當沒有傳遞引數時,通過閉包的old變數,我們可以一路向上找到這個方法.
function () {
console.log(this.userName + '---->' + 'show1')
}
複製程式碼
結語
就這樣,我們通過閉包中的old變數,將對不傳引數,傳了一個引數和傳了兩個引數進行區別處理的方法給串聯了起來.實現了js的過載.再次感嘆一下,這個方法真的很巧妙.真是漂亮又充滿魅力的程式碼
如果覺得還可以,請點贊鼓勵一下,謝謝!