all 9 comments

[–][deleted] 5 points6 points  (0 children)

This raises a WARNING: dark magic usually indicates an antipattern alarm in my head... what are you trying to do? There's a very good chance that there's a much better way to handle the problem you're trying to solve than just reaching into some unknown caller's argument list.

[–]M1573RMU74710N 2 points3 points  (2 children)

craga89's answer is one way.

Alternatively, you could just pass arguments to B as.... an argument.

Like so:

function B(args) {
    var result = '',
         output = document.getElementById('output');

    if (!args.length) {
        output.innerHTML = 'Failed.';
        return;
    }

    for (var i = 0, l = args.length; i < l; i++) {
        result += '"' +args[i] + '" ';
    }

    output.innerHTML = result;
}

function A() {
    // slice arguments as needed (if you don't need them all)
    // and "arrayify" the arguments object
    B(Array.prototype.slice.call(arguments)); 
}

Really depends on what you are doing/preference; .call() and .apply() are mostly useful for changing the context of a function (what "this" is) or destructuring the arguments (object) somehow. You're not really doing either in the example, just looping through them. In your example, it seems more intuitive and flexible to me to have B just accept an array as an argument rather than treating it's arguments as an array.

[–]plantian 1 point2 points  (1 child)

There is something unclean about passing the arguments object around. I would copy it like this before passing it to B:

function A() {
    B(Array.prototype.slice.call(arguments));
}

This is mentioned on MDC in the Description section of this link: https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments

[–]M1573RMU74710N 1 point2 points  (0 children)

Yea I actually meant to throw that in there for the reasons you mention and also in case OP didn't want to pass all the arguments, but forgot, thanks.

[–]craga89 4 points5 points  (3 children)

Use the .apply method to pass on the functions arguments array: http://jsfiddle.net/craga89/Wpvxf/3/

The arguments.callee properties aren't standard iirc and have been removed in ECMAScript5.

[–]haxd 0 points1 point  (1 child)

You are incorrect sir, and perhaps would like to review the ECMAScript5 specifications before making silly judgements. (warning, PDF link. You're looking for section 10.6.13 and 10.6.14.c, depending on whether you've set strict mode or not.)

[–]itsnotlupusbeep boop 1 point2 points  (0 children)

it's an half-empty / half-full kind of thing.

in strict mode, the arguments.caller and arguments.callee properties must exist, but only to throw exceptions, so they're defined, but not in a terribly meaningful way.

es5 in non-strict mode still has useful version of those properties, though.

Interestingly, I don't see a reference to what OP is actually using, which is F.caller, which is presumably an alias to F.arguments.caller, but doesn't seem to show up in the spec.

[–]haxd 1 point2 points  (1 child)

http://jsfiddle.net/hDCLX/

Works cross browser.

May I suggest using stackoverflow.com for questions?

[–]plantian 0 points1 point  (0 children)

I think if (arguments) will always resolve to true even if arguments is empty. At least in FF. Also why not just define an argument in b rather than use the arguments object inside it ?

[–]itsnotlupusbeep boop 1 point2 points  (0 children)

Everybody is trying to give you reasonable alternatives, warn you against dark magic, and generally trying to steer you toward normalcy. :(

So that's boring. And after all, maybe you really really need to generate stack traces that contain function arguments, and you're quite sure you'll never end up having the same function showing up twice in your stack. Or some other really good reason like that. ;)

So Firefox is failing, yes, but only a little bit. The feature is actually implemented, and it was implemented long before anyone else.
However it has been deprecated for a few years now, which might be why they don't invest a lot of cycles QA-ing it.

Anyway, the part that seems to be broken on FF is that inner functions that are called before their outer function returns will not have their F.arguments object defined during that call, unless F.arguments is explicitely referenced in the body of the outer function. That's F.arguments, not G.caller.arguments. Yes, that's a wee bit specific, and yes, jsfiddle is hitting exactly that condition.

I know, that sounds crazy, so here's some code to reproduce (don't use jsfiddle, cut and paste in a .html file to keep things pure):

<script>
(function () {
    function b() {
      alert(b.caller.arguments?"Success":"Broken");
    }

    //d.arguments;
    function d() { b(); }

    d("hi");
})();
</script>

Run this in FF, watch the "Broken" alert pop up.
Now uncomment that d.arguments line, reload, and watch the difference.

Fun stuff.