Parsing function arguments

Benchmark created on


Setup

/**
 * Parses the parameters of a function.toString() and returns them as an object
 * @param {*} fn The function to get the parameters from
 * @param {*} asArray If true, it returns an array instead of an object
 * @returns An object (or array) with the parameters and their default values
 * @author [bye-csavier](https://github.com/bye-csavier)
 */
function getFnArgs(fn, asArray = false){

    if(!(typeof fn === 'function')) return null;

	fn = fn.toString();
	fn = fn.replace(/\s/gm, ''); //remove all white spaces
	fn = fn.match(/(?<=\()(.+?)(?=\))/); //get the function arguments section
	if(fn == null) return null; else fn = fn[0];
	fn = fn.replace(/(\/\/.*$)|(\/\*[\s\S]*?\*\/)/g, '') //removes comments
	fn = fn.replace(/(?<!\\)["']/g, '`') //simplifies string delimiters for less checks
	
	let subStr = ''; let parsedArgs = "{"; let delimitersTest = {};

	let openDelimiter = [];
	
	const strToArg = (str) => {
		parsedArgs += `'${str}':${str},`;
	}

	delimitersTest.open = "";
	delimitersTest.close = "";

	//recursive RegEx is not supported therefore this is the best I can do
	fn.replace(/\{(.+?)\}|\[(.+?)\]|\((.+?)\)|`(.+?)`/g, (match, p1, p2, p3, p4) => {
		if(p1){ delimitersTest.open += "{"; delimitersTest.close += "}"; }
		else if(p2){ delimitersTest.open += "["; delimitersTest.close += "]"; }
		else if(p3){ delimitersTest.open += "("; delimitersTest.close += ")"; }
		else if(p4){ delimitersTest.open += "`"; delimitersTest.close += "`"; }
		
		return match;
	});
	delimitersTest.isOpening = (char) => {
		if(delimitersTest.open.indexOf(char) > -1){
			openDelimiter.push(char);
		}
	}
	delimitersTest.isClosing = (char) => {
		let idx = delimitersTest.close.indexOf(char);
		if(idx < 0) return;
		if(delimitersTest.open.indexOf(openDelimiter[openDelimiter.length-1]) == idx){
			openDelimiter.pop();
		}
	}

	let argList = fn; //save a copy of the original argList for later evaluation
	fn = (fn+',').split('') // string to array, and added trailing comma to push also the last argument
	let len = fn.length, CAN_READ = true;
	for(let i=0; i<len; i++){

		if(fn[i] == '=') CAN_READ = false;
		else if(openDelimiter.length > 0){
			if(openDelimiter.length > 0) delimitersTest.isClosing(fn[i])
		}
		else if(openDelimiter.length == 0 && (fn[i] == ',')){
			strToArg(subStr);
			subStr = '';
			CAN_READ = true;
		}
		else{
			if(delimitersTest.isOpening(fn[i])) CAN_READ = false;
			if(CAN_READ) subStr += fn[i];
		}

	}
	parsedArgs += '}';

	fn = `((${argList})=>{return ${parsedArgs}})();`;
	parsedArgs = eval(fn);

	if(!asArray) return parsedArgs;
	return Object.values(parsedArgs);
}
function test(a, 
	b=2, c={a:2, b:'lol'}, dee, e=[2,3,"a"],/*lol joke=2,2*/ foook=e, g=`test, ok`, h={a:'ok \"', b:{c:'lol=2,tricky=2', d:'lol'}}, i=[2,5,['x']] //ajaim
) {
	return {
		a: a,
		b: b,
		c: c,
		dee: dee,
		e: e,
		foook: foook,
		g: g,
		h: h,
		i: i
	}
}

Test runner

Ready to run.

Testing in
TestOps/sec
parsing function arguments
getFnArgs(test)
ready
-
""
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.