Javascript查找传递给函数的参数

时间:2011-07-16 05:14:53

标签: javascript argument-passing

我需要找到从函数传递给函数的参数。

让我们假设我有一个名为foo的函数:

function foo() {
  var a = 3;
  var b = "hello";
  var c = [0,4];
  bar(a - b / c);
  bar(c * a + b);
}

function bar(arg) { alert(arg) }

现在,当然,bar将始终提醒NaN

在函数bar内部,我希望以最初传递的形式获取参数。此外,我希望能够从a函数访问bcbar的值。换句话说,我想要这种性质:

bar(a - b / c);    

function bar() {
 //some magic code here
 alert(originalArg); //will alert "a - b / c"
 alert(vars.a + " " + vars.b + " " + vars.c); //will alert "3 hello 0,4"
} 

您可能认为这不可行,但我知道您可以使用Javascript做一些奇怪的事情。例如,您可以像这样显示调用函数的代码:

function bar() {
  alert(bar.caller);
}

我愿意下注几美元,通过某种形式,您可以获得参数的原始形式和参数中变量的值。

如果您被允许以这样的形式致电bar,我可以很容易地看到您可以这样做:

bar(function(){return a - b / c}, {a: a, b: b, c: c});

但这太复杂,因此是不可接受的。调用bar的方式可能不会改变。

6 个答案:

答案 0 :(得分:6)

首先 - 奇怪的问题。它会帮助 来了解您所询问的原因的上下文,因为很明显Javascript没有完全您所要求的内容。 (例如,它是否与数学表达式有关?是否必须在调用函数之前执行表达式,如示例所示?)

Javascript没有:

  • 命名参数
  • 一流/动态表情

Javascript 确实

  • 动态对象
  • 一流的功能
  • eval

动态对象

由于这是你自己声明的最后选项,你宣称这是不可接受的,听起来它对你没什么帮助。在任何情况下,由于您需要表达式这些值,仅仅传递值的对象并不会对您有所帮助。

一流的功能

正如您所说,您可以访问Function.caller,尽管它只输出整个函数的String表示。它也是非标准的。

您还有arguments,它是传入的参数数组,未命名。对于现有代码,显然,它将是表达式的执行结果。 arguments的一个方便用法是不在函数签名中指定任何参数,然后作为开放式列表迭代数组。

闭包

这也是最后一个不可接受的解决方案的一部分,但可能最适合您的工作解决方案,尽管您必须在bar被称为的方式上保持灵活性在哪里定义。

function foo() {
  var a = 3;
  var b = "hello";
  var c = [0,4];

  var bar = function(arg) {
    alert(arg); // NaN
    alert(a + " " + b + " " + c); //will alert "3 hello 0,4"
  }

  bar(a - b / c);
  bar(c * a + b);
}

EVAL

使用eval和闭包,你可能会接近你想要的。

要记住的主要事情是,在您的代码中,a - b / c是一个表达式。那是参数,表达式的结果是参数。除了函数本身的toString之外还有 ,它会提醒“a - b / c”。

使用eval免责声明:这非常黑客且不推荐!您已收到警告。),您实际上可以传递字符串作为参数,并使用绑定到空间的闭包,提醒您想要的内容。

function foo() {
  var a = 3;
  var b = "hello";
  var c = [0,4];

  function hackyUglyEvalAndOutput(callback, expression) {
    alert(expression); //will alert "a - b / c" (duh)
    alert(a + " " + b + " " + c); //will alert "3 hello 0,4" (because of the closure)
    callback(eval(expression)); // "NaN"
  }
  hackyUglyEvalAndOutput(bar, "a - b / c");
  hackyUglyEvalAndOutput(bar, "c * a + b");
}

function bar(arg) { alert(arg); }

您可以类似地解析caller以查找对函数的调用,并查看传递给参数列表的内容(我确实得到了它)但是您无法区分哪一个如果有多个,则调用。从你的例子来看,这对于完成你想做的事情至关重要。

再次 - 表达只是那个,仅此而已;他们不是争论。

P.S。对bar的第二次调用实际上会输出NaNhello:)

答案 1 :(得分:4)

好的,我做到了! (有点)你在下面看到的是我写过的最天真,最奇怪,最丑陋,最丑陋的代码。这是我第一次真正使用正则表达式,而且我不擅长编写解析器代码。

我们走了:

    function foo () {
        var a = 3;
        var b = "hello";
        var c = [0, 4];
        bar(a - b / c); 
        bar(c * a + b);
    };

    var callTracker = {};
    function bar() {
        var caller = bar.caller.toString();
        callTracker[caller] !== undefined ? callTracker[caller]++ : callTracker[caller] = 0;
        function findCall(str) {
            return str.search(/bar\((\S*\s*)*\);/);
        }

        //Step 1: Get the orginal form of the argument
        var callers = [caller];
        var index = 0;
        var len;
        while (true) {
            len = callers.length - 1;
            index = findCall(callers[len]);
            if (index === -1) break;
            callers.push(callers[len].substring(index + 1));
        }
        var callIndex = callTracker[caller] % len;
        var thisCall = callers[callIndex];
        var argument = thisCall.substring(findCall(thisCall));
        argument = argument.slice(4, argument.search(/\);/));
        alert(argument);

        //Step 2: Get the values of the variables
        caller = caller.slice(caller.search(/\{/) + 1, -1);
        var lines = caller.split(";");
        for (var i = 0; i < lines.length; i++) {
            lines[i] += ";";
            if (findCall(lines[i]) === -1) {
                eval(lines[i]);
            }
        }
        var vars = argument.match(/\w/g);
        for (var i in vars) {
            if (vars.hasOwnProperty(i)) {
                alert(eval(vars[i]));
            }
        }
    }

    foo();

我可以预见到很多情况会破坏,但我太轻松了,不能测试它们。举几个例子:如果代码包含一个字符串文字,中间有分号,它会破坏,或者如果有人依赖自动分号插入,它会破坏。如果在对return的两次调用之间存在条件bar语句,则由于我非常天真的解析,它将会中断。

但它确实适用于这个例子。如果在一个真实的项目中使用,我认为它实际上可以在大多数情况下工作,但最终可能会出现一些非常奇怪的错误。

如果你想自己玩,here it is on JsFiddle

我认为,最后一件事就是永远不要低估能够阅读(甚至改变)自己的程序的力量

答案 2 :(得分:3)

就个人而言,我正在考虑生成一个异常然后跟踪堆栈跟踪,但这只能在firefox上运行..幸运的是有人编写了一个堆栈跟踪库,它总是指示调用哪个函数,这样你就可以解析出确切的行了。呼叫。

https://github.com/eriwen/javascript-stacktrace/blob/master/stacktrace.js

答案 3 :(得分:3)

你的问题被误导和生病......我喜欢它!这是一个简短的解决方案,我将解释。

function foo() {
    var a = 3, b = "hello", c = [0, 4];
    bar(a - b / c); 
}

function bar(n) {
    alert([n, a, b, c, eval(n)].join('   '));
}

function meld(f, g) {
    f = f.toString(); g = g.toString();
    f = f.replace(/function\s+\w+/, 'function ');

    var n = g.match(/function\s+(\w+)/)[1];
    f = f.replace(new RegExp(n + '\\((.*?)\\)', 'g'), n + '("$1")');

    var i = f.indexOf('{') + 1;
    f = f.slice(0, i) + g + f.slice(i);

    eval('var ret = ' + f);
    return ret;
}

foo = meld(foo, bar);
foo();

密钥是meld(f, g)函数,它返回一个新的匿名函数,该函数在调用f并将其内部暴露给g的副本时就像g一样。您看,原始f处于不利位置,无法查看有关g.caller的任何内容 - 最多可以使用meld和大量正则表达式。

以下是f的伪代码。首先使function foo() {成为一个匿名函数,适合于稍后评估和赋值给变量;例如function() {变为g。接下来找到f的真实姓名,并引用从新g传递给它的任何参数。接下来,在新f中插入f的副本。它将掩盖全局名称,它将有权访问meld的局部变量,就好像它们是自己的一样。最后,评估此匿名函数并将其返回。

那么我们如何使用foo?只需将meld(foo, bar)替换为foo,然后像往常一样使用meld

好的,现在为了限制。我不想花费大量精力来改进g内的正则表达式来引用{{1}}的所有可能性的论点。这只会分散整个解决方案的概念。你需要改变它以正确处理多个参数并转义它们自己有引号或括号的任何参数。

答案 4 :(得分:2)

我对该主题的意见:

http://jsfiddle.net/eWBpF/2/

function foo () {
    var a = 3;
    var b = "hello";
    var c = [5, 2];
    var d = new Date();
    bar(a - b / c);
    bar(c * a + b - d);
};

function bar(args)
{
    var caller = bar.caller.toString();

    if( typeof window.bar_call == 'undefined' )
    {
        window.bar_call = 0;
    }
    else
    {
        window.bar_call++;
    }

    var indexes = caller.match(/var \S* = [^;]+;/g);
    var varValues = [];
    for( var i=0; i<indexes.length; i++ )
    {
        var tmpVal = indexes[i].substring(indexes[i].search(/=/));
        tmpVal = tmpVal.substring(2,tmpVal.length-1);

        varValues.push(tmpVal)
    }
    var variables = varValues.join("  ");

    var barCalls = caller.match(/bar\([^;]+\);/g);

    var call = barCalls[window.bar_call];
    call = call.substring(4, call.length-2);

    alert(call + "\n\n" + variables);
}

foo();

答案 5 :(得分:1)

这将是整洁的,但这是不可能的。 最接近的是arguments,它只是当前函数的所有参数的数组。 您可以枚举此列表并派生类型和值,并获取长度,以便知道有多少参数 - 但名称和运算符将丢失。

请参阅http://www.devguru.com/technologies/ecmascript/quickref/arguments.html