作為一個每天與JavaScript打交道的前端開發,雖然明知這樣的問題沒人看,但還是忍不住要答一波以正視聽。
既然題主強調是“形參(形式引數,formal parameters)”,那我覺得回答說用arguments物件的肯定都不對。arguments是“實參(實際引數,actual parameters)”。
有了以上的概念,再來看題主的問題“javascript如何獲取function的形參”。題主其實是想做類似反射的操作,給定一個函式變數,然後得到該函式定義時的引數宣告列表。
首先,如果題主並非真的想拿到每個實參的詳細資訊,而是隻需要實參個數,那麼簡單的使用length屬性就可以了。這個方式,對箭頭函式(arrow function)也是適用的。
如果題主真的想拿到每個實參的詳細資訊,那就比較有意思了,因為JavaScript裡目前並沒有提供直接的方式來實現這個操作,甚至最新的Reflect也幫不上什麼忙。好在function的toString方法可以以字串方式返回函式定義時的程式碼,這就提供了一個突破口(jiu ming dao cao),可以從function的定義裡自行解析出引數列表。
而這也是流行的框架AngularJS實現基於形參的依賴注入(DI)的關鍵。這裡直接給出AngularJS裡的相關程式碼片段。
從程式碼中可以看到,該方法呼叫了toString拿到function的定義,使用正則來去除註釋並提取形參列表。該實現也同時具備處理箭頭函式的能力。
可以看到,這個方法相當的高效,而且相容性也相當不錯,即使引數中存在預設值、解構、可變引數等ES6的新語法,也不會出錯,但也不會給出進一步的資訊。
如果需要提取引數預設值、分析解構、或者判斷是否為可變引數,則需要進一步的逐個分析,這裡不再展開討論。但有個相當簡便的方法是,使用JavaScript語法分析器來解析,例如esprima,但可能會有較大的效能開銷(可參考https://stackoverflow.com/questions/1007981/how-to-get-function-parameter-names-values-dynamically)。
另外,如果程式碼經過壓縮,則這種方法就失效了。Angular.js的解決方法是在需要注入的地方,接收一個手動輸入的陣列作為函式的引數描述(也可以透過給函式設定靜態屬性提供注入資訊),從而不受壓縮的影響。但這樣增加了開發人員的工作量(每個引數列表都需要重複兩遍)。因此,有人開發了叫做ng-annotate的工具,可以整合的構建工具裡自動為方法新增引數描述資訊。
作為一個每天與JavaScript打交道的前端開發,雖然明知這樣的問題沒人看,但還是忍不住要答一波以正視聽。
既然題主強調是“形參(形式引數,formal parameters)”,那我覺得回答說用arguments物件的肯定都不對。arguments是“實參(實際引數,actual parameters)”。
形參: 函式宣告時指定的引數宣告列表。實參: 函式呼叫時傳遞進來的引數列表。有了以上的概念,再來看題主的問題“javascript如何獲取function的形參”。題主其實是想做類似反射的操作,給定一個函式變數,然後得到該函式定義時的引數宣告列表。
首先,如果題主並非真的想拿到每個實參的詳細資訊,而是隻需要實參個數,那麼簡單的使用length屬性就可以了。這個方式,對箭頭函式(arrow function)也是適用的。
如果題主真的想拿到每個實參的詳細資訊,那就比較有意思了,因為JavaScript裡目前並沒有提供直接的方式來實現這個操作,甚至最新的Reflect也幫不上什麼忙。好在function的toString方法可以以字串方式返回函式定義時的程式碼,這就提供了一個突破口(jiu ming dao cao),可以從function的定義裡自行解析出引數列表。
而這也是流行的框架AngularJS實現基於形參的依賴注入(DI)的關鍵。這裡直接給出AngularJS裡的相關程式碼片段。
從程式碼中可以看到,該方法呼叫了toString拿到function的定義,使用正則來去除註釋並提取形參列表。該實現也同時具備處理箭頭函式的能力。
可以看到,這個方法相當的高效,而且相容性也相當不錯,即使引數中存在預設值、解構、可變引數等ES6的新語法,也不會出錯,但也不會給出進一步的資訊。
如果需要提取引數預設值、分析解構、或者判斷是否為可變引數,則需要進一步的逐個分析,這裡不再展開討論。但有個相當簡便的方法是,使用JavaScript語法分析器來解析,例如esprima,但可能會有較大的效能開銷(可參考https://stackoverflow.com/questions/1007981/how-to-get-function-parameter-names-values-dynamically)。
另外,如果程式碼經過壓縮,則這種方法就失效了。Angular.js的解決方法是在需要注入的地方,接收一個手動輸入的陣列作為函式的引數描述(也可以透過給函式設定靜態屬性提供注入資訊),從而不受壓縮的影響。但這樣增加了開發人員的工作量(每個引數列表都需要重複兩遍)。因此,有人開發了叫做ng-annotate的工具,可以整合的構建工具裡自動為方法新增引數描述資訊。