📜 ⬆️ ⬇️

Javascript function argument schema or C-style prototypes without heavyweight frameworks

Many were faced with the need to use optional function arguments. If such an argument is one, and even the last one, then there are no particular problems.

function set(name, value){ if(value == undefined){ value = name; } ... } 


The task is complicated if the last two arguments may be missing. Naturally they must be of a different type. A typical case is when the last argument is a colback, and the last but one option or set of options. For example, the send method sends some data to a certain destination. It is necessary to transfer only the data itself to it, but it is possible to clarify the method of transfer using a set of options and transfer a callback that will be called after the request is completed.

 function send(data){ if(arguments[1] instanceof Function){ var callback = arguments[1]; var options = null; } else if(arguments[2] instanceof Function){ var callback = arguments[2]; var options = arguments[1]; } ... } 

')
Now we complicate still. The addressee is no longer a certain one, it needs to be specified using the id parameter and at the same time there is a desire to send a packet of requests to different addressees. What to do? The logic of the function is sharpened by our, almost deterministic, set of arguments, therefore, without particularly going into details (since we have already forgotten what has been written there), we wrap it (logic) into an internal function and put the call into the loop. Now the inner function takes an initial set of arguments,
and with a set of external again need to be cunning.

 function send(){ function __send(id, data){ ... } if(arguments[0] instanceof Array){ var pack = arguments[0]; var callback = arguments[1]; for(var i=0; i<pack.length; i++){ __send.apply(this, pack[i]); } } } 


pack is a two-dimensional array.

It would seem that everything, the tricks are over, but life throws up another argument, and it is common for the whole pack. Approximately such a scenario of events development led me to the realization of the need for a simpler and more beautiful way of analyzing arguments. Short thoughts led me to the use of C-like prototypes. The idea is this. We make a class whose constructor accepts arguments, and its checkin method checks whether the arguments correspond to one or another prototype.
To make it clear how this works, to begin with, I give an example of use. New argument is called without furthering, newArg.

 function send(){ function __send(){ var args = new argSchema(arguments); if(args.checkin('number id', 'object data', 'opt bool newArg', 'opt function callback')){ //  args.id, args.data, args.newArg, args.callback ... } ... } var args = new argSchema(arguments); if(args.checkin('array pack', 'opt bool newArg', 'opt function callback')){ for(var i=0; i<pack.length; i++){ __send.apply(this, args.pack[i]); } } else if(args.checkin('number id', 'object data', 'opt bool newArg', 'opt function callback')){ __send(args.id, args.data, args.newArg, args.callback); } } 


And if life throws us another optional argument, then it will be possible to attach it without fear of breaking the logic of the analysis of arguments.

Now the code itself:

 var is = function( type, obj ) { return Object.prototype.toString.call( obj ) === "[object "+ type +"]"; } is['string'] = function(obj){ return (typeof obj == 'string' || obj instanceof String); } is.number = function(obj){ return (typeof obj == 'number' || obj instanceof Number); } is.bool = function(obj){ return (typeof obj == 'boolean' || obj instanceof Boolean); } is.object = function(obj){ return (typeof obj == 'object'); } is.array = function(obj){ return (obj instanceof Array); } is.func = function(obj){ return (obj instanceof Function); } is.set = function(obj){ return (obj != undefined && obj != null); } is.unset = function(obj){ return !this.set(obj); } function argSchema(argArr){ this.checkin = function(){ var arr, qual, type, name; function check(type, arg){ if(is.unset(arg)) return true; switch(type){ case 'string': if(is.string(arg)){ this[name] = arg; } else return false; break; case 'number': if(is.number(arg)){ this[name] = arg; } else return false; break; case 'bool': if(is.bool(arg)){ this[name] = arg; } else return false; break; case 'object': if(is.object(arg)){ this[name] = arg; } else return false; break; case 'function': if(is.func(arg)){ this[name] = arg; } else return false; break; case 'array': if(is.array(arg)){ this[name] = arg; } else return false; break; } return true; } for(var i=0, j=0; i<arguments.length; i++){ arr = arguments[i].split(' '); qual = type = name = null; if(arr.length == 3){ qual = arr[0]; type = arr[1]; name = arr[2]; } else if(arr.length == 2){ type = arr[0]; name = arr[1]; } delete this[name]; if(qual == 'opt'){ if(check.call(this, type, argArr[j])){ j++; } } else{ if(!check.call(this, type, argArr[j])) return false; j++; } } return true; } } 


The examples in the text are purely explanatory and have not been tested for operability. But here you can see a working example.

Source: https://habr.com/ru/post/195816/


All Articles