讓JavaScript 輕松支持函數(shù)重載 (Part 1 - 設(shè)計(jì))
更新時(shí)間:2009年08月04日 00:18:17 作者:
JavaScript支持函數(shù)重載嗎?可以說不支持,也可以說支持。說不支持,是因?yàn)镴avaScript不能好像其它原生支持函數(shù)重載的語言一樣,直接寫多個(gè)同名函數(shù),讓編譯器來判斷某個(gè)調(diào)用對(duì)應(yīng)的是哪一個(gè)重載。
JavaScript支持重載嗎?
JavaScript支持函數(shù)重載嗎?可以說不支持,也可以說支持。說不支持,是因?yàn)镴avaScript不能好像其它原生支持函數(shù)重載的語言一樣,直接寫多個(gè)同名函數(shù),讓編譯器來判斷某個(gè)調(diào)用對(duì)應(yīng)的是哪一個(gè)重載。說支持,是因?yàn)镴avaScript函數(shù)對(duì)參數(shù)列表不作任何限制,可以在函數(shù)內(nèi)部模擬對(duì)函數(shù)重載的支持。
實(shí)際上,在很多著名的開源庫當(dāng)中,我們都可以看到函數(shù)內(nèi)部模擬重載支持的設(shè)計(jì)。例如說jQuery的jQuery.extend方法,就是通過參數(shù)類型判斷出可選參數(shù)是否存在,如果不存在的話就對(duì)參數(shù)進(jìn)行移位以確保后面的邏輯正確運(yùn)行。我相信很多人在寫JavaScript時(shí)也寫過類似的代碼,以求為功能豐富的函數(shù)提供一個(gè)(或多個(gè))簡單的調(diào)用入口。
不過做種做法一個(gè)根本的問題,那就是違反了DRY原則。每個(gè)支持重載的函數(shù)內(nèi)部都多出來一段代碼,用于根據(jù)參數(shù)個(gè)數(shù)和參數(shù)類型處理重載,這些代碼暗含著重復(fù)的邏輯,寫出來卻又每一段都不一樣。此外,這些代碼要維護(hù)起來也不容易,因?yàn)殚喿x代碼時(shí)你并不能一眼看出函數(shù)支持的幾種重載方式是什么,要對(duì)重載做出維護(hù)自然也困難。
描述重載入口的DSL
我希望能夠在JavaScript中以一種簡單的方式來描述重載入口。最好就如同在其它語言中一樣,使用函數(shù)簽名來區(qū)分重載入口,因?yàn)槲艺J(rèn)為函數(shù)簽名就是這方面最好的DSL。我假想中最符合JavaScript語法的重載入口描述DSL應(yīng)該是這樣子的:
var sum = new Overload();
sum.add("Number, Number",
function(x, y) { return x + y; });
sum.add("Number, Number, Number",
function(x, y, z) { return x + y + z; });
在描述好重載入口與對(duì)應(yīng)函數(shù)體后,對(duì)sum函數(shù)的調(diào)用應(yīng)該是這樣子的:
sum(1, 2);
sum(1, 2, 3);
上述代碼在我看來非常清晰,也非常容易維護(hù)——你可以一眼看得出重載入口的簽名,并且要修改或者增加重載入口都是很容易的事情。但是我們遇到了一個(gè)問題,那就是JavaScript里面的函數(shù)是不能new出來的,通過new Overload()獲得的對(duì)象一定不能被調(diào)用,為此我們只能把Overload做成一個(gè)靜態(tài)類,靜態(tài)方法返回的是Function實(shí)例:
var sum = Overload
.add("Number, Number",
function(x, y) { return x + y; })
.add("Number, Number, Number",
function(x, y, z) { return x + y + z; });
必要的重載入口支持
想象一下,有哪些常見的JavaScript函數(shù)入口是用上述DSL無法描述的?我所知道的有兩種:
任意類型參數(shù)
假想我們要寫一個(gè)each函數(shù),對(duì)于Array就迭代它的下標(biāo),對(duì)于其它類型就迭代它的所有成員,這兩個(gè)函數(shù)入口的參數(shù)列表如何聲明?如果用C#,我們會(huì)如此描述兩個(gè)函數(shù)入口:
void Each(IEnumerable iterator) { }
void Each(object iterator) { }
然而在JavaScript當(dāng)中,Object不是一切類型的基類,(100) instanceof Object的結(jié)果為false,所以我們不能用Object來指代任意類型,必須引入一個(gè)新的符號(hào)來指代任意類型。考慮到這個(gè)符號(hào)不應(yīng)該與任何可能存在的類名沖突,所以我選擇了用"*"來表示任意類型。上述C#代碼對(duì)應(yīng)的JavaScript應(yīng)該是這樣子的:
var each = Overload
.add("Array",
function(array) { })
.add("*",
function(object) { });
任意數(shù)量參數(shù)
在JavaScript的函數(shù)里面,要求支持任意數(shù)量參數(shù)是很常見的需求,相信使用率比C#里面的params關(guān)鍵字要多得多。在我們之前制定的規(guī)則當(dāng)中,這也無法描述的,因此我們要引入一個(gè)不和類名沖突的符號(hào)來表示C#中的params。我選擇了用"..."表示params,意思是這里出現(xiàn)任意多個(gè)參數(shù)都是可以接受的。讓我們看看jQuery.extend的重載應(yīng)該如何描述:
var extend = Overload
.add("*, ...",
function(target) { })
.add("Boolean, *, ...",
function(deep, target) { });
小結(jié)
在這篇文章當(dāng)中,我們嘗試設(shè)計(jì)出一種適用于JavaScript且易讀易維護(hù)的函數(shù)重載寫法。在下一篇文章當(dāng)中,我們將會(huì)嘗試編寫Overload類,以實(shí)現(xiàn)這一設(shè)計(jì)。
JavaScript支持函數(shù)重載嗎?可以說不支持,也可以說支持。說不支持,是因?yàn)镴avaScript不能好像其它原生支持函數(shù)重載的語言一樣,直接寫多個(gè)同名函數(shù),讓編譯器來判斷某個(gè)調(diào)用對(duì)應(yīng)的是哪一個(gè)重載。說支持,是因?yàn)镴avaScript函數(shù)對(duì)參數(shù)列表不作任何限制,可以在函數(shù)內(nèi)部模擬對(duì)函數(shù)重載的支持。
實(shí)際上,在很多著名的開源庫當(dāng)中,我們都可以看到函數(shù)內(nèi)部模擬重載支持的設(shè)計(jì)。例如說jQuery的jQuery.extend方法,就是通過參數(shù)類型判斷出可選參數(shù)是否存在,如果不存在的話就對(duì)參數(shù)進(jìn)行移位以確保后面的邏輯正確運(yùn)行。我相信很多人在寫JavaScript時(shí)也寫過類似的代碼,以求為功能豐富的函數(shù)提供一個(gè)(或多個(gè))簡單的調(diào)用入口。
不過做種做法一個(gè)根本的問題,那就是違反了DRY原則。每個(gè)支持重載的函數(shù)內(nèi)部都多出來一段代碼,用于根據(jù)參數(shù)個(gè)數(shù)和參數(shù)類型處理重載,這些代碼暗含著重復(fù)的邏輯,寫出來卻又每一段都不一樣。此外,這些代碼要維護(hù)起來也不容易,因?yàn)殚喿x代碼時(shí)你并不能一眼看出函數(shù)支持的幾種重載方式是什么,要對(duì)重載做出維護(hù)自然也困難。
描述重載入口的DSL
我希望能夠在JavaScript中以一種簡單的方式來描述重載入口。最好就如同在其它語言中一樣,使用函數(shù)簽名來區(qū)分重載入口,因?yàn)槲艺J(rèn)為函數(shù)簽名就是這方面最好的DSL。我假想中最符合JavaScript語法的重載入口描述DSL應(yīng)該是這樣子的:
復(fù)制代碼 代碼如下:
var sum = new Overload();
sum.add("Number, Number",
function(x, y) { return x + y; });
sum.add("Number, Number, Number",
function(x, y, z) { return x + y + z; });
在描述好重載入口與對(duì)應(yīng)函數(shù)體后,對(duì)sum函數(shù)的調(diào)用應(yīng)該是這樣子的:
sum(1, 2);
sum(1, 2, 3);
上述代碼在我看來非常清晰,也非常容易維護(hù)——你可以一眼看得出重載入口的簽名,并且要修改或者增加重載入口都是很容易的事情。但是我們遇到了一個(gè)問題,那就是JavaScript里面的函數(shù)是不能new出來的,通過new Overload()獲得的對(duì)象一定不能被調(diào)用,為此我們只能把Overload做成一個(gè)靜態(tài)類,靜態(tài)方法返回的是Function實(shí)例:
復(fù)制代碼 代碼如下:
var sum = Overload
.add("Number, Number",
function(x, y) { return x + y; })
.add("Number, Number, Number",
function(x, y, z) { return x + y + z; });
必要的重載入口支持
想象一下,有哪些常見的JavaScript函數(shù)入口是用上述DSL無法描述的?我所知道的有兩種:
任意類型參數(shù)
假想我們要寫一個(gè)each函數(shù),對(duì)于Array就迭代它的下標(biāo),對(duì)于其它類型就迭代它的所有成員,這兩個(gè)函數(shù)入口的參數(shù)列表如何聲明?如果用C#,我們會(huì)如此描述兩個(gè)函數(shù)入口:
void Each(IEnumerable iterator) { }
void Each(object iterator) { }
然而在JavaScript當(dāng)中,Object不是一切類型的基類,(100) instanceof Object的結(jié)果為false,所以我們不能用Object來指代任意類型,必須引入一個(gè)新的符號(hào)來指代任意類型。考慮到這個(gè)符號(hào)不應(yīng)該與任何可能存在的類名沖突,所以我選擇了用"*"來表示任意類型。上述C#代碼對(duì)應(yīng)的JavaScript應(yīng)該是這樣子的:
復(fù)制代碼 代碼如下:
var each = Overload
.add("Array",
function(array) { })
.add("*",
function(object) { });
任意數(shù)量參數(shù)
在JavaScript的函數(shù)里面,要求支持任意數(shù)量參數(shù)是很常見的需求,相信使用率比C#里面的params關(guān)鍵字要多得多。在我們之前制定的規(guī)則當(dāng)中,這也無法描述的,因此我們要引入一個(gè)不和類名沖突的符號(hào)來表示C#中的params。我選擇了用"..."表示params,意思是這里出現(xiàn)任意多個(gè)參數(shù)都是可以接受的。讓我們看看jQuery.extend的重載應(yīng)該如何描述:
復(fù)制代碼 代碼如下:
var extend = Overload
.add("*, ...",
function(target) { })
.add("Boolean, *, ...",
function(deep, target) { });
小結(jié)
在這篇文章當(dāng)中,我們嘗試設(shè)計(jì)出一種適用于JavaScript且易讀易維護(hù)的函數(shù)重載寫法。在下一篇文章當(dāng)中,我們將會(huì)嘗試編寫Overload類,以實(shí)現(xiàn)這一設(shè)計(jì)。
相關(guān)文章
Echarts中X軸/Y軸坐標(biāo)標(biāo)簽顯示不下的問題解決
本文主要介紹了Echarts中X軸/Y軸坐標(biāo)標(biāo)簽顯示不下的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09
JavaScript實(shí)現(xiàn)移動(dòng)端滑動(dòng)選擇日期功能
這篇文章主要介紹了JavaScript實(shí)現(xiàn)滑動(dòng)選擇日期功能,基于sui-mobile的移動(dòng)端實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-06-06
BootStrap使用file-input插件上傳圖片的方法
這篇文章主要介紹了BootStrap使用file-input插件上傳圖片的方法,bootstrap的圖片上傳框架 file-input 插件非常不錯(cuò),下面小編通過本文介紹下這個(gè)插件的使用方法,感興趣的朋友一起看看吧2016-09-09
極力推薦一款小巧玲瓏的可視化編輯器bootstrap-wysiwyg
這篇文章主要為大家極力推薦一款小巧玲瓏的可視化編輯器bootstrap-wysiwyg,是一款基于jquery和bootstrap的可視化編輯器,感興趣的小伙伴們可以參考一下2016-05-05
JavaScript實(shí)現(xiàn)好看的跟隨彩色氣泡效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)好看的跟隨彩色氣泡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02
JavaScript 使用Ckeditor+Ckfinder文件上傳案例詳解
這篇文章主要介紹了JavaScript 使用Ckeditor+Ckfinder文件上傳案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
微信小程序chooseImage的用法(從本地相冊(cè)選擇圖片或使用相機(jī)拍照)
這篇文章主要介紹了微信小程序chooseImage的用法(從本地相冊(cè)選擇圖片或使用相機(jī)拍照),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08

