2

I have this call:

function del(Model, modelName, model_id, req, res, next, cb) {

    if(req.query.optimisticDelete){
        optimisticDelete(arguments);
    }
    else{
        pessimisticDelete(arguments);
    }
}

the problem of course, is that the arguments aren't passed correctly to the optimisticDelete and pessimisticDelete functions.

in an ideal JS world, this might work, but I can easily see why it doesn't.

But it doesn't take away from that fact that I just didn't want to type all the arguments out for each call, and in fact I wanted to omit the arguments in the del function signature also, so this would be the most ideal situation, although I need a reference to the req object, which I am now missing:

  function del() {

        if(req.query.optimisticDelete){
            optimisticDelete(arguments);
        }
        else{
            pessimisticDelete(arguments);
        }
    }

but of course, when arguments is passed, it does not seem to magically separate into separate arguments.

And also, this doesn't work either:

function del(Model, modelName, model_id, req, res, next, cb) {

    if(req.query.optimisticDelete){
        optimisticDelete(Array.prototype.slice.call(arguments));
    }
    else{
        pessimisticDelete(Array.prototype.slice.call(arguments));
    }
}

if you understand what I am trying to do, please let me know if it's possible and how,

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • `optimisticDelete.apply(null, Array.prototype.slice.call(arguments));` – Ben Taber Aug 21 '15 at 01:29
  • 1
    @BenTaber You don't need to convert `arguments` to an array before using it with `apply`. – Barmar Aug 21 '15 at 01:30
  • 1
    Reading up on what `arguments` are would be a great start to this question https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments and then searching previous questions would be the best followup. – Jason Cust Aug 21 '15 at 01:31

2 Answers2

6

You're calling optimisticDelete with a single argument, which is an arguments special object that contains all the arguments to the original call. If you want to spread them out, you need to use apply:

optimisticDelete.apply(null, arguments);

It's not necessary to convert arguments to an array first. MDN says:

You can also use arguments for the argsArray parameter. arguments is a local variable of a function. It can be used for all unspecified arguments of the called object. Thus, you do not have to know the arguments of the called object when you use the apply method. You can use arguments to pass all the arguments to the called object.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    According to ECMAScript 2015, [*Function.prototype.apply*](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-function.prototype.apply) will convert *arguments* to a List, which seems to be a much simpler structure than an Array. So a good reason to pass *arguments* as–is is to not waste time converting it to an Array that will be converted to a List anyway. ;-) – RobG Aug 21 '15 at 02:45
5

There are two problems. First, the arguments object is weird, but it's not that weird. Nothing magically turns into a parameter list across a simple function call. There is, however, Function.prototype.apply, which is ultimately what you want.

First however you'll want to turn arguments into a plain array:

var plainArgs = Array.prototype.slice.call(arguments);

Or, if you care about runtime efficiency:

var plainArgs = [];
for (var i = 0; i < arguments.length; ++i)
  plainArgs[i] = arguments[i];

(Note — this step may not be necessary, but passing the arguments object out of a function tends to make optimizers throw up their little hands and give up on your functions.)

(Another note — totally ignore this stuff about passing arguments to .apply() being bad. I'm wrong.)

With that out of the way, you can use .apply():

    optimisticDelete.apply(null, plainArgs);

The .apply() function expects its second argument to be an array. That array becomes the individual arguments to the function. The first argument to .apply() is the value you'd like this to take on in the function call; since you were calling optimisticDelete() "naked" in the original code, I passed null, but you can pass whatever you want.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    You don't have to convert it to an array first. – Barmar Aug 21 '15 at 01:31
  • @Barmar are you sure about that? I have a reptile-brain memory of having to do that, but maybe that was Netscape 4 or something. (And you certainly do have to do it if you don't want V8 to bail on optimization.) – Pointy Aug 21 '15 at 01:32
  • I quoted the paragraph about this in my answer. – Barmar Aug 21 '15 at 01:33
  • 1
    While the syntax of `apply()` is almost identical to that of `call()`, the fundamental difference is that `call()` accepts an argument list, while `apply()` accepts a single array of arguments. – Sphvn Aug 21 '15 at 01:34
  • @Barmar OK then, I'm convinced. However I still try to keep `arguments` from being passed out of my functions, maybe because in cases like this it's often something kind-of low-level that may get called a lot. – Pointy Aug 21 '15 at 01:35
  • @Sphvn yes, that is certainly true. – Pointy Aug 21 '15 at 01:35
  • 1
    @Sphvn the problem is that the semantics of the `arguments` object are weird in that the indexed elements of `arguments` are aliases for the actual parameters of a function, so it makes static analysis kind-of impossible. If you pass `arguments` to another function, V8 simply will not attempt to optimize your function at all. I suspect that other optimizers behave the same way for the same reasons. – Pointy Aug 21 '15 at 01:38
  • 1
    @Sphvn [here is an interesting article on the topic.](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments) – Pointy Aug 21 '15 at 01:39
  • @Sphvn digressing from the topic, but it seems that `try ... catch` also cause V8 to drop your functions from optimization consideration! – Pointy Aug 21 '15 at 01:41
  • @Pointy, thanks for link! A topic of great interest that I have been wanting to learn about :) – AmmarCSE Aug 21 '15 at 01:44
  • @AmmarCSE That article is just about V8, but given the highly competitive and yet collaborative nature of JavaScript runtime/optimization developers, I would be very surprised to learn that the other runtime systems are much different. – Pointy Aug 21 '15 at 01:46
  • Valid point. Thanks for the extra info. – Sphvn Aug 21 '15 at 01:50
  • 1
    @Pointy, interesting point in the article https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage, specifically, `Never use arguments directly without .length or [i] (STRICTLY x.apply(y, arguments) is ok, nothing else is , e.g. .slice. Function#apply is special)` Wouldnt that mean that using `apply` like in Barmars answer wouldnt hurt optimization? – AmmarCSE Aug 21 '15 at 01:51
  • @AmmarCSE yes it would imply that my entire line of argument here has been completely wrong :) Live and learn. – Pointy Aug 21 '15 at 01:55
  • @Pointy, heh, trust me, I have learned much more than you today :) , thanks again – AmmarCSE Aug 21 '15 at 01:55
  • @Pointy, instead of doing the for loop, wouldn't it be better to rely on the array slice api and do something like this? var plainArgs = [].slice.call(arguments) – KumarM Aug 21 '15 at 02:07
  • 1
    @KumarM Well though I was wrong about `.apply()`, it's *generally* true that passing `arguments` out of a function causes the optimizer to give up on optimization. Read that github link I posted a few comments above this. The `.apply()` function is special, but others are not. – Pointy Aug 21 '15 at 02:10
  • @Pointy, thanks for the link. Makes sense. – KumarM Aug 21 '15 at 02:20
  • @Pointy—in mathematics (and hence computing), a *general* solution is one that works everywhere (e.g. general theory of relativity), so you probably mean *usually*. ;-) – RobG Aug 21 '15 at 02:35
  • @RobG yea this was not a good question for me – Pointy Aug 21 '15 at 04:28