我正在開發一個JavaScript庫,供第三方開發人員使用。 API包括與此簽名的方法:我們應該在JavaScript API中驗證方法參數嗎?
函數doSomething的(ARG1,ARG2,選項)
- ARG1,ARG2是 '必要' 簡單類型的參數。
- 選項是一個包含可選參數的哈希對象。
您會建議驗證: - 參數類型是否有效? - 選項屬性是否正確?例如:開發者沒有錯誤地通過onSucces而不是onSuccess?
- 爲什麼流行的庫如prototype.js不驗證?
我正在開發一個JavaScript庫,供第三方開發人員使用。 API包括與此簽名的方法:我們應該在JavaScript API中驗證方法參數嗎?
函數doSomething的(ARG1,ARG2,選項)
您會建議驗證: - 參數類型是否有效? - 選項屬性是否正確?例如:開發者沒有錯誤地通過onSucces而不是onSuccess?
感謝您的詳細解答。
下面是我的解決方案 - 驗證實用程序對象,可以很容易地擴展到基本上驗證任何東西... 代碼仍然足夠短,以便我不需要在生產中解析它。
WL.Validators = {
/*
* Validates each argument in the array with the matching validator.
* @Param array - a JavaScript array.
* @Param validators - an array of validators - a validator can be a function or
* a simple JavaScript type (string).
*/
validateArray : function (array, validators){
if (! WL.Utils.isDevelopmentMode()){
return;
}
for (var i = 0; i < array.length; ++i){
WL.Validators.validateArgument(array[i], validators[i]);
}
},
/*
* Validates a single argument.
* @Param arg - an argument of any type.
* @Param validator - a function or a simple JavaScript type (string).
*/
validateArgument : function (arg, validator){
switch (typeof validator){
// Case validation function.
case 'function':
validator.call(this, arg);
break;
// Case direct type.
case 'string':
if (typeof arg !== validator){
throw new Error("Invalid argument '" + Object.toJSON(arg) + "' expected type " + validator);
}
break;
}
},
/*
* Validates that each option attribute in the given options has a valid name and type.
* @Param options - the options to validate.
* @Param validOptions - the valid options hash with their validators:
* validOptions = {
* onSuccess : 'function',
* timeout : function(value){...}
* }
*/
validateOptions : function (validOptions, options){
if (! WL.Utils.isDevelopmentMode() || typeof options === 'undefined'){
return;
}
for (var att in options){
if (! validOptions[att]){
throw new Error("Invalid options attribute '" + att + "', valid attributes: " + Object.toJSON(validOptions));
}
try {
WL.Validators.validateArgument(options[att], validOptions[att]);
}
catch (e){
throw new Error("Invalid options attribute '" + att + "'");
}
}
},
};
的我如何使用它下面有幾個例子:
isUserAuthenticated : function(realm) {
WL.Validators.validateArgument(realm, 'string');
getLocation: function(options) {
WL.Validators.validateOptions{
onSuccess: 'function',
onFailure: 'function'}, options);
makeRequest : function(url, options) {
WL.Validators.validateArray(arguments, ['string',
WL.Validators.validateOptions.carry({
onSuccess : 'function',
onFailure : 'function',
timeout : 'number'})]);
不驗證。更多的代碼是用戶必須下載的更多代碼,因此這對用戶和生產系統來說是非常實際的成本。參數錯誤很容易被開發人員捕獲;不要給用戶帶來這樣的負擔。
您有權決定是否進行「防禦」與「契約」API。在很多情況下,閱讀庫的手冊可以讓用戶明白,他應該提供符合這些和那些約束的這種或那種類型的參數。
如果你打算做一個非常直觀的,用戶友好的API,至少在調試模式下驗證你的參數是很好的。但是,驗證需要花費時間(並且源代碼=>空格),所以它可能也不錯。
這取決於你。
這取決於。這個圖書館有多大?據說類型化語言對於具有複雜API的大型項目更好。由於JS在一定程度上是混合的,你可以選擇。
關於驗證 - 我不喜歡防禦性編程,函數的用戶應該有義務傳遞有效的參數。並在JS代碼大小的問題。
儘可能地進行驗證並打印有用的錯誤消息,這些消息可幫助人們快速輕鬆地找到問題。
引用此驗證碼並附帶一些特殊註釋(如//+++VALIDATE
和//--VALIDATE
),以便您可以使用高速壓縮生產版本的工具輕鬆刪除它。
高速生產失敗? –
在生產代碼中,由於大多數錯誤已得到修復,故障早期通常不再那麼重要。也就是說,如果您使用我的方法,您可以輕鬆創建較慢的驗證版本,並在需要時將其部署到生產環境中。 –
當我在過去開發類似這樣的API時,我已驗證任何我認爲是「主要」的要求 - 在您的示例中,我會驗證前兩個參數。
只要您指定了合理的默認值,對於您的用戶來說,確定「可選」參數沒有被正確指定是非常簡單的,因爲它不會對應用程序進行任何更改,但一切仍然可行正常。
如果API很複雜,我會建議遵循Aaron的建議 - 在驗證過程中添加可由壓縮器解析的註釋,以便開發人員獲得驗證的好處,但可以在推送代碼時提取額外的體重投入生產。
編輯:
我這裏還有一些我喜歡在驗證是必要的情況下做的例子。這個特例非常簡單;我可能不會爲它的驗證而煩惱,因爲它確實很微不足道。根據您的需要,有時試圖強制類型比驗證更好,如整數值所示。
假設延伸()是一個合併的對象的功能,並且所述的輔助函數存在:
var f = function(args){
args = extend({
foo: 1,
bar: function(){},
biz: 'hello'
}, args || {});
// ensure foo is an int.
args.foo = parseInt(args.foo);
//<validation>
if(!isNumeric(args.foo) || args.foo > 10 || args.foo < 0){
throw new Error('foo must be a number between 0 and 10');
}
if(!isFunction(args.bar)){
throw new Error('bar must be a valid function');
}
if(!isString(args.biz) || args.biz.length == 0){
throw new Error('biz must be a string, and cannot be empty');
}
//</validation>
};
編輯2:
如果要避免常見的拼寫錯誤,則可以:1)接受和重新分配它們或2)驗證參數計數。選項1是容易的,選擇2可以做到這樣的,不過我肯定它重構爲它自己的方法,像Object.extendStrict()(例如代碼工作瓦特/原型):
var args = {
ar: ''
};
var base = {
foo: 1,
bar: function(){},
biz: 'hello'
};
// save the original length
var length = Object.keys(base).length;
// extend
args = Object.extend(base, args || {});
// detect if there're any extras
if(Object.keys(args).length != length){
throw new Error('Invalid argument specified. Please check the options.')
}
中間方法是在缺少必要的參數時返回合理的默認值(例如null)。這樣,用戶的代碼就會失敗,而不是你的。他們可能會更容易找出他們的代碼而不是你的代碼中的問題。
您可以通過合同同時驗證項目(參數)和出口(返程)點。這裏接受JSDoc語法合同一個lib https://www.npmjs.com/package/bycontract:
/**
* @param {number|string} sum
* @param {Object.<string, string>} payload
* @param {function} cb
* @returns {HTMLElement}
*/
function foo(sum, payload, cb) {
// Test if the contract is respected at entry point
byContract(arguments, [ "number|string", "Object.<string, string>", "function" ]);
// ..
var res = document.createElement("div");
// Test if the contract is respected at exit point
return byContract(res, "HTMLElement");
}
// Test it
foo(100, { foo: "foo" }, function(){}); // ok
foo(100, { foo: 100 }, function(){}); // exception
但是我一直在開發/測試/開發環境的這種驗證,但跳過它活:
if (env === "production") {
byContract.isEnabled = false;
}
這是一個好主意。最重要的是,你甚至可以'裝飾'一個函數來驗證它的參數,並保留原始函數'clean'。 – xtofl