Javascript 如何动态获取函数参数名称/值?
有没有办法动态获取函数的函数参数名 假设我的函数如下所示:Javascript 如何动态获取函数参数名称/值?,javascript,reflection,function-parameter,Javascript,Reflection,Function Parameter,有没有办法动态获取函数的函数参数名 假设我的函数如下所示: function doSomething(param1, param2, .... paramN){ // fill an array with the parameter name and value // some other code } getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"] 现
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]
现在,我如何从函数内部将参数名及其值的列表获取到数组中?我不知道如何获取参数列表,但您可以这样做来获取它期望的数量
alert(doSomething.length);
我以前试过这样做,但从来没有找到一个实用的方法来完成它。我最终传入了一个对象,然后在其中循环
//define like
function test(args) {
for(var item in args) {
alert(item);
alert(args[item]);
}
}
//then used like
test({
name:"Joe",
age:40,
admin:bool
});
我不知道这个解决方案是否适合您的问题,但它允许您重新定义所需的任何函数,而无需更改使用它的代码。现有调用将使用定位参数,而函数实现可能使用“命名参数”(单个散列参数) 我认为您无论如何都会修改现有的函数定义,因此,为什么不使用一个工厂函数来实现您想要的:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var withNamedParams = function(params, lambda) {
return function() {
var named = {};
var max = arguments.length;
for (var i=0; i<max; i++) {
named[params[i]] = arguments[i];
}
return lambda(named);
};
};
var foo = withNamedParams(["a", "b", "c"], function(params) {
for (var param in params) {
alert(param + ": " + params[param]);
}
});
foo(1, 2, 3);
</script>
</head>
<body>
</body>
</html>
var withNamedParams=函数(参数,λ){
返回函数(){
var name={};
var max=arguments.length;
对于(var i=0;i,可以使用“arguments”属性访问传递给函数的参数值
function doSomething()
{
var args = doSomething.arguments;
var numArgs = args.length;
for(var i = 0 ; i < numArgs ; i++)
{
console.log("arg " + (i+1) + " = " + args[i]);
//console.log works with firefox + firebug
// you can use an alert to check in other browsers
}
}
doSomething(1, '2', {A:2}, [1,2,3]);
函数doSomething()
{
var args=doSomething.arguments;
var numArgs=args.length;
对于(变量i=0;i
这里有一种方法:
// Utility function to extract arg name-value pairs
function getArgs(args) {
var argsObj = {};
var argList = /\(([^)]*)/.exec(args.callee)[1];
var argCnt = 0;
var tokens;
var argRe = /\s*([^,]+)/g;
while (tokens = argRe.exec(argList)) {
argsObj[tokens[1]] = args[argCnt++];
}
return argsObj;
}
// Test subject
function add(number1, number2) {
var args = getArgs(arguments);
console.log(args); // ({ number1: 3, number2: 4 })
}
// Invoke test subject
add(3, 4);
注意:这仅适用于支持参数的浏览器。被调用方
以下函数将返回传入的任何函数的参数名数组
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if(result === null)
result = [];
return result;
}
用法示例:
getParamNames(getParamNames) // returns ['func']
getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d']
getParamNames(function (){}) // returns []
编辑:
随着ES6的发明,此功能可以通过默认参数触发。以下是一个在大多数情况下都可以使用的快速破解:
var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
我说大多数情况是因为有些事情会把事情搞砸
function (a=4*(5/3), b) {} // returns ['a']
编辑:
我还注意到vikasde也希望数组中的参数值。这已经在名为arguments的局部变量中提供
摘录自:
arguments对象不是数组。它类似于数组,但除了长度之外没有任何数组属性。例如,它没有pop方法。但是它可以转换为实数组:
var args = Array.prototype.slice.call(arguments);
如果阵列泛型可用,则可以使用以下方法:
var args = Array.slice(arguments);
我通常如何做:
function name(arg1, arg2){
var args = arguments; // array: [arg1, arg2]
var objecArgOne = args[0].one;
}
name({one: "1", two: "2"}, "string");
您甚至可以通过函数名引用参数,如:
name.arguments;
希望这能有所帮助!下面的代码取自AngularJS,它将该技术用于依赖项注入机制
下面是一个来自
Angular的依赖项注入器为控制器提供服务
当构造控制器时。依赖关系注入器也
负责创建服务可能存在的任何可传递依赖项
have(服务通常依赖于其他服务)
请注意,参数的名称很重要,因为
使用这些来查找依赖项
//请参见:
//全局变量,命名为bB
var-bB=5;
//依赖注入控制器
变量a=函数(str,fn){
//弦化函数体
var fnStr=fn.toString();
//键:将表单参数获取为字符串
var args=fnStr.match(/function\s*\(.*?)/);
//
console.log(args);
//如果表单arg为“bB”,则执行它,否则,不执行任何操作
对于(变量i=0;i
这很简单
首先是一个弃用的参数。被调用方
——对被调用函数的引用。
第二步,如果你有一个函数的引用,你可以很容易地得到它们的文本表示。
第三步,如果您以构造函数的形式调用函数,还可以通过yourObject.constructor创建一个链接。
注意:第一个解决方案已被弃用,因此如果您不能不使用它,您还必须考虑您的应用程序架构。
如果不需要精确的变量名,只需在函数内部使用内部变量参数
,无需任何魔法
它们都将调用toString并替换为re,这样我们就可以创建一个助手:
// getting names of declared parameters
var getFunctionParams = function (func) {
return String(func).replace(/[^\(]+\(([^\)]*)\).*/m, '$1');
}
一些例子:
// Solution 1. deprecated! don't use it!
var myPrivateFunction = function SomeFuncName (foo, bar, buz) {
console.log(getFunctionParams(arguments.callee));
};
myPrivateFunction (1, 2);
// Solution 2.
var myFunction = function someFunc (foo, bar, buz) {
// some code
};
var params = getFunctionParams(myFunction);
console.log(params);
// Solution 3.
var cls = function SuperKewlClass (foo, bar, buz) {
// some code
};
var inst = new cls();
var params = getFunctionParams(inst.constructor);
console.log(params);
享受JS
UPD:Jack Allan实际上得到了一个更好的解决方案。GJ Jack!不太容易出现空格和注释错误的解决方案是:
var fn = function(/* whoa) */ hi, you){};
fn.toString()
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1]
.split(/,/)
["hi", "you"]
=>[“a”、“b”、“c”]您还可以使用“esprima”解析器来避免参数列表中的注释、空格和其他问题
function getParameters(yourFunction) {
var i,
// safetyValve is necessary, because sole "function () {...}"
// is not a valid syntax
parsed = esprima.parse("safetyValve = " + yourFunction.toString()),
params = parsed.body[0].expression.right.params,
ret = [];
for (i = 0; i < params.length; i += 1) {
// Handle default params. Exe: function defaults(a = 0,b = 2,c = 3){}
if (params[i].type == 'AssignmentPattern') {
ret.push(params[i].left.name)
} else {
ret.push(params[i].name);
}
}
return ret;
}
从@jack allan I获得的命令稍微修改了该函数,以允许ES6默认属性,例如:
function( a, b = 1, c ){};
仍然返回['a','b']
/**
* Get the keys of the paramaters of a function.
*
* @param {function} method Function to get parameter keys for
* @return {array}
*/
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
var result = argsList.match( ARGUMENT_NAMES );
if(result === null) {
return [];
}
else {
var stripped = [];
for ( var i = 0; i < result.length; i++ ) {
stripped.push( result[i].replace(/[\s,]/g, '') );
}
return stripped;
}
}
/**
*获取函数参数的键。
*
*@param{function}方法函数以获取
*@return{array}
*/
var STRIP\u COMMENTS=/(\/\/.$)|(\/\*[\s\s]*?\*\/)/mg;
变量参数名称=/(?:^ |,)\s*([^\s,=]+)/g;
函数getFunctionParameters(func){
var fnStr=func.toString().replace(带注释“”);
var argsList=fnStr.slice(fnStr.indexOf('(')+1,fnStr.indexOf('));
var result=argsList.match(参数名称);
如果(结果===null){
返回[];
}
否则{
var=[];
对于(变量i=0;i
无论采用何种解决方案,它都不能破坏wierd函数,其toString()
看起来与wierd一样:
function ( A, b
,c ,d
){}
getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]
function( a, b = 1, c ){};
/**
* Get the keys of the paramaters of a function.
*
* @param {function} method Function to get parameter keys for
* @return {array}
*/
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
var result = argsList.match( ARGUMENT_NAMES );
if(result === null) {
return [];
}
else {
var stripped = [];
for ( var i = 0; i < result.length; i++ ) {
stripped.push( result[i].replace(/[\s,]/g, '') );
}
return stripped;
}
}
function ( A, b
,c ,d
){}
function getArguments(f) {
return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}
function $args(func) {
return (func + '')
.replace(/[/][/].*$/mg,'') // strip single-line comments
.replace(/\s+/g, '') // strip white space
.replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments
.split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters
.replace(/=[^,]+/g, '') // strip any ES6 defaults
.split(',').filter(Boolean); // split & filter [""]
}
'function (a,b,c)...' // returns ["a","b","c"]
'function ()...' // returns []
'function named(a, b, c) ...' // returns ["a","b","c"]
'function (a /* = 1 */, b /* = true */) ...' // returns ["a","b"]
'function fprintf(handle, fmt /*, ...*/) ...' // returns ["handle","fmt"]
'function( a, b = 1, c )...' // returns ["a","b","c"]
'function (a=4*(5/3), b) ...' // returns ["a","b"]
'function (a, // single-line comment xjunk) ...' // returns ["a","b"]
'function (a /* fooled you...' // returns ["a","b"]
'function (a /* function() yes */, \n /* no, */b)/* omg! */...' // returns ["a","b"]
'function ( A, b \n,c ,d \n ) \n ...' // returns ["A","b","c","d"]
'function (a,b)...' // returns ["a","b"]
'function $args(func) ...' // returns ["func"]
'null...' // returns ["null"]
'function Object() ...' // returns []
function doSomething(foo, bar) {
console.log("does something");
}
function doSomething() {
var foo = arguments[0];
var bar = arguments[1];
console.log("does something");
}
function saySomething(obj) {
if(obj.message) console.log((obj.sender || "Anon") + ": " + obj.message);
}
saySomething({sender: "user123", message: "Hello world"});
const babylon = require("babylon")
function doSomething(a, b, c) {
// get the values of passed argumenst
const argValues = arguments
// get the names of the arguments by parsing the function
const ast = babylon.parse(doSomething.toString())
const argNames = ast.program.body[0].params.map(node => node.name)
// join the 2 arrays, by looping over the longest of 2 arrays
const maxLen = Math.max(argNames.length, argValues.length)
const args = []
for (i = 0; i < maxLen; i++) {
args.push({name: argNames[i], value: argValues[i]})
}
console.log(args)
// implement the actual function here
}
doSomething(1, 2, 3, 4)
[
{
"name": "a",
"value": 1
},
{
"name": "c",
"value": 3
},
{
"value": 4
}
]
function do_tests(func) {
if (typeof func !== 'function') return true;
switch (typeof func.tests) {
case 'undefined' : return true;
case 'object' :
for (var k in func.tests) {
var test = func.tests[k];
if (typeof test==='function') {
var result = test(func);
if (result===false) {
console.log(test.name,'for',func.name,'failed');
return false;
}
}
}
return true;
case 'function' :
return func.tests(func);
}
return true;
}
function strip_comments(src) {
var spaces=(s)=>{
switch (s) {
case 0 : return '';
case 1 : return ' ';
case 2 : return ' ';
default :
return Array(s+1).join(' ');
}
};
var c1 = src.indexOf ('/*'),
c2 = src.indexOf ('//'),
eol;
var out = "";
var killc2 = () => {
out += src.substr(0,c2);
eol = src.indexOf('\n',c2);
if (eol>=0) {
src = spaces(eol-c2)+'\n'+src.substr(eol+1);
} else {
src = spaces(src.length-c2);
return true;
}
return false;
};
while ((c1>=0) || (c2>=0)) {
if (c1>=0) {
// c1 is a hit
if ( (c1<c2) || (c2<0) ) {
// and it beats c2
out += src.substr(0,c1);
eol = src.indexOf('*/',c1+2);
if (eol>=0) {
src = spaces((eol-c1)+2)+src.substr(eol+2);
} else {
src = spaces(src.length-c1);
break;
}
} else {
if (c2 >=0) {
// c2 is a hit and it beats c1
if (killc2()) break;
}
}
} else {
if (c2>=0) {
// c2 is a hit, c1 is a miss.
if (killc2()) break;
} else {
// both c1 & c2 are a miss
break;
}
}
c1 = src.indexOf ('/*');
c2 = src.indexOf ('//');
}
return out + src;
}
function function_args(fn) {
var src = strip_comments(fn.toString());
var names=src.split(')')[0].replace(/\s/g,'').split('(')[1].split(',');
return names;
}
function_args.tests = [
function test1 () {
function/*al programmers will sometimes*/strip_comments_tester/* because some comments are annoying*/(
/*see this---(((*/ src//)) it's an annoying comment does not help anyone understand if the
,code,//really does
/**/sucks ,much /*?*/)/*who would put "comment\" about a function like (this) { comment } here?*/{
}
var data = function_args(strip_comments_tester);
return ( (data.length==4) &&
(data[0]=='src') &&
(data[1]=='code') &&
(data[2]=='sucks') &&
(data[3]=='much') );
}
];
do_tests(function_args);
new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
function getParameters(func) {
return new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}
var getParameters = func => new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
function foo(abc, def, ghi, jkl) {
//code
}
function ( A, b
,c ,d
){}
function(a /* fooled you)*/,b){}
function test(arg1,arg2){
var funcStr = test.toString()
var leftIndex = funcStr.indexOf('(');
var rightIndex = funcStr.indexOf(')');
var paramStr = funcStr.substr(leftIndex+1,rightIndex-leftIndex-1);
var params = paramStr.split(',');
for(param of params){
console.log(param); // arg1,arg2
}
}
test();
if (result[0] === '{' && result[result.length - 1 === '}']) result = result.slice(1, -1)
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
const ast = parser.parse("(\n" + func.toString() + "\n)")
const acorn = require('acorn');
function f(a, b, c) {
// ...
}
const argNames = acorn.parse(f).body[0].params.map(x => x.name);
console.log(argNames); // Output: [ 'a', 'b', 'c' ]
npm install reflect-metadata
const AnyDecorator = () : MethodDecorator => {
return target => { }
}
class Person{
@AnyDecorator()
sayHello(other: Person){}
}
const instance = new Person();
const funcType = Reflect.getMetadata('design:type', instance.sayHello);
const funcParams = Reflect.getMetadata('design:paramtypes', instance.sayHello);