方法重載是根據形參的數量、類型不同而調用相應的同名方法。
JavaScript 方法本身是不存在方法重載的,后一個方法會覆蓋前面的同名方法:
function fun() {
return 1;
}
function fun(param) {
return 2;
}
fun(); //2
fun(0); //2
JavaScript 中實現函數重載,主要有兩種途徑(沒有考慮到參數的類型):
- 利用arguments類數組來判斷實參個數
- 利用閉包保存之前注冊進來的同名函數
通過arguments實現
arguments.length
可以獲得實參的數量,函數名.length
可以獲取形參的數量。
這里我們可以實現通過實參的數量不同來實現不同的處理。
function fun() {
if(arguments.length == 0) {
return 1;
} else if(arguments.length == 1) {
return 2;
}
}
fun(); //1
fun(0); //2
閉包實現方法重載
閉包是指有權訪問另一個函數作用域變量的函數,創建閉包的通常方式,是在一個函數內部創建另一個函數。
想深入了解閉包可以看這篇文章,講得很好。
先來個網上很容易搜到的閉包實現方法重載的例子
function addMethod (object, name, fn) {
// 把前一次添加的方法存在一個臨時變量old中
var old = object[name];
// 重寫object[name]方法
object[name] = function () {
if (fn.length === arguments.length) {
// 若是調用object[name]方法時,若是實參和形參個數一致,則直接調用
return fn.apply(this, arguments);
} else if (typeof old === 'function') {
// 若是實參形參不一致,判斷old是不是函數,若是,就調用old
return old.apply(this, arguments);
}
};
}
var people = {
values: ['Dean Edwards', 'Sam Stephenson', 'Alex Russell', 'Dean Tom']
};
//find all values
addMethod(people, 'find', function() {
return this.values;
});
//find value by fisrtName
addMethod(people, 'find', function(firstName) {
return this.values.filter((value) => {
return value.indexOf(firstName) !== -1 ? true : false;
});
});
//find value by firstName and lastName
addMethod(people, 'find', function(firstName, lastName) {
return this.values.filter((value) => {
var fullName = `${firstName} ${lastName}`;
return value.indexOf(fullName) !== -1 ? true : false;
});
});
console.log(people.find()); // ["Dean Edwards", "Sam Stephenson", "Alex Russell", "Dean Tom"]
console.log(people.find('Dean')); // ["Dean Edwards", "Dean Tom"]
console.log(people.find('Dean', 'Edwards')); // ["Dean Edwards"]
現在來深入解析下這個例子,方便理解閉包如何實現方法重載。
可以看到 addMethod
方法被調用了三次,每一次調用都會產生一個新的匿名函數,並且這個新的匿名函數通過閉包包含着一個 old
變量和fn
函數。
addMethod
方法在每次執行時,都會生成一個 addMethod
的活動變量和執行環境,執行完畢以后完后都只是銷毀了執行環境,但活動對象由於被閉包函數所引用,仍然保留。old
中包含之前舊的方法。這樣就像洋蔥一樣一層一層。通過閉包訪問old
和fn
來實現函數的重載。
當第一次調用 addMethod
時,people["find"]
沒有定義,所以 old
被賦值為 undefined(不妨記為old1),然后開始重寫people["find"]
,不妨記為find1,以便理解。然后addMethod
執行完畢,由於old
和 fn
(記為fn1)被 people["find"]
方法引用,所以活動對象仍然保留。
當第二次調用 addMethod
時,people["find"]
是find1,所以 old
被賦值為 find1(需要注意的是這個old是一個新的局部變量,不妨記為old2),然后開始重寫people["find"]
,不妨記為find2。然后addMethod
執行完畢,由於old
和 fn
(記為fn2)被 `people["find"]方法引用,所以活動對象仍然保留。
當第三次調用 addMethod
時,people["find"]
是find2,所以 old
被賦值為 find2 (記為old3),然后開始重寫people["find"]
,不妨記為find3。然后addMethod
執行完畢,由於old
和 fn
(記為fn3)被 `people["find"]方法引用,所以活動對象仍然保留。
當我們調用 people.find()
時,由於此時的 people.find
是 find3,實參個數為0,fn3的形參個數為2,兩者個數不等,所以 執行old.apply(this, arguments);
。這個 old
是 old3,值是find2。
find2 中 fn2 的形參個數為 1,也不同,所以執行old.apply(this, arguments);
這個 old
是 old2,值是find1。
find1 中 fn1 的形參個數為 0,實參和形參個數相同,所以執行fn.apply(this, arguments);
相信你如果對閉包有所了解,還是很容易理解上面的每一步的。