0

I'm modifying several functions of some "core" JS software using "hijacking":

var orig_func = func; // initially function(a,b,c,d)

func = function(a,b,c,d) {
    // modify arguments
    d = someCustomization(c,d);
    // do additional stuff
    doSomethingCustom(a);
    // apply the original "core" function
    return orig_func.apply(this,arguments);
}

which works nicely unless the modified params are optional: calling

func(a,b);

works in a way that d is undefined even though someCustomization returns something else when both its arguments are undefined.

Here's an MVCE for using in cosole:

var f = function(optionalParam){ if(optionalParam) console.log(optionalParam); };
var orig_f = f;
f = function(optionalParam){
    optionalParam = optionalParam ? 'custom: '+optionalParam : 'default';
    orig_f.apply(this, arguments);
};
f(); // shows undefined
f('test'); // shows 'custom: test'

"Expected" behaviour is to see "default" in console in the first case, but I get undefined instead.

After some experiments and using this thread where I've summarized adding a positioned param in a corresponding answer I've come with the following solution in terms of MCVE:

var f = function(some,optionalParam){
    console.log('some:',some);
    console.log('optionalParam:',optionalParam);
};
var orig_f = f;
f = function(some,optionalParam){
    if(optionalParam)
        optionalParam = 'custom: '+optionalParam;
    else {
        var argPosition = 2;
        while(arguments.length < argPosition)
            [].push.call(arguments,undefined);
        arguments[argPosition-1] = 'default';
    }
    orig_f.apply(this, arguments);
};
f(); // shows 'some: undefined' and 'optionalParam: default'

or in terms of the initial task:

var orig_func = func; // initially function(a,b,c,d)

func = function(a,b,c,d) {
    // modify arguments
    while(arguments.length < 4)
        [].push.call(arguments,undefined);
    arguments[3] = someCustomization(c,d);
    // do additional stuff
    doSomethingCustom(a);
    // apply the original "core" function
    return orig_func.apply(this,arguments);
}

But I'm not really able to explain what's the problem with the initial approach: why it worked for required (used) params and failed for an optional (unused) one? Does it have anything to do with closures? Why d is "not connected" with arguments in the second case?

1
  • You're not actually modifying arguments, you just assign to the local variables? The "connection" to the arguments object is deprecated and only works in sloppy mode. Don't use that. Commented Mar 17, 2018 at 19:04

2 Answers 2

3

arguments "magic bindings" are created by iterating actual arguments, so if an arg is not there, no binding will be created:

function a(x, y) {
    y = 42;
    console.log(arguments)
}

a(10, 20)
a(10)

That said, using magic bindings is a bad idea, and they won't work in the strict mode anyways. If the number of arguments is known beforehand, simply do

orig_func.call(this, a, b, c, d);

otherwise use a splat ...args and manipulate that.

Sign up to request clarification or add additional context in comments.

Comments

0

Right, with the help of georg ("magic bindings" keyword) and some more research I've come to an almost sufficient explanation:

  • in the old standart (haven't found which one, though) argument variables (a,b,c,d) and arguments[i] point to the same values (not sure how this is done in JS, though)

    • in the new standart this is not so (like Bergi has said, it is deprecated): changing arguments[0] won't affect a and vise versa; like georg said, it is so in the strict mode:

      function f(x) {
          "use strict";
          arguments[0] = 5;
          console.log( x );
      }
      f(1); // logs 1
      
  • now when all variables are present, both a,b,c,d (as variables scoped to the function) and arguments[1-4] are defined and connected pairwise pointing to the same values. When params c and d are omitted, arguments[3] and arguments[4] are not defined and even if we define them, there's no "connection" between them and the variables (again, I'm not sure if such connection can be created manually)

  • basic recomendation is if we need to modify arguments, we should either use only arguments (and .apply them) or only local variables (and use .call) or mix them but don't rely on the connection between them

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.