5

Problem: A Javascript function needs few parameters to work with:

function kick(person, reason, amount) {
    // kick the *person* with the *amount*, based on the *reason*
}

As there's no way to do function overloading in JS like how you do in Java, if it needs to be designed for easy future improvement (parameters adding), it can be written as:

/* Function Parameters pattern */
function kick() {
    // kick the person as in *arguments[0]*, with the amount as in *arguments[1]*,
    // based on the reason as in *arguments[2]*, with the strength as in *arguments[3]*
}

or

/* Object Configuration Pattern */
function kick(config) {
    // kick the person as in *config.person*, with the amount as in *config.amount*,
    // based on the reason as in *config.reason*, with the strength as in *config.strength*
}

I do know that Object Configuration Pattern allows augmentation for any default properties.

So, the question is: If I don't need to augment any properties with the parameters, is there any significant reason of using any one of the proposed solutions as opposed to the other?

Community
  • 1
  • 1
Kenny Ki
  • 3,400
  • 1
  • 25
  • 30

3 Answers3

4

Using an object has a few advantages:

1. The code is more readable

Consider the following two calls:

kick({user: u,
      reason: "flood",
      log: true,
      rejoin: false,
      timeout: 60000,
      privmessage: true});

kick(u, "flood", true, false, 60000, true);

and imagine someone else reading the call. What is the first true? Note also that yourself in a few month would be in the same exact position (not remembering what is the fourth parameter to kick is very similar to not knowing it).

2. You can tunnel parameters

With the object approach you can pass a function a set of parameters that this function must use to call another function

function kickgroup(users, parms) {
    for (var i=0; i<users.lenght; i++) {
        var uparms = Object.create(parms);
        uparms.user = users[i];
        kick(uparms);
    }
}

Note also that in the arguments case you don't need to punish yourself by using arguments[x] syntax. You can just declare the parameters and add them as the function evolves: any parameter that has not been passed will be set to undefined (and if you need you can still access arguments.length to distinguish if the caller explicitly passed your function undefined).

6502
  • 112,025
  • 15
  • 165
  • 265
  • great one about the readability! But what do you mean by deriving object from `parms`? – Kenny Ki Sep 19 '11 at 06:57
  • @KennyKi: In Javascript it's possible to create an object X that will have all attributes of Y and that when you change an attribute of X you are not changing Y. Doing so is not trivial (requires some play with `prototype`) but allows to receive a configuration and passing a variation of this configuration (e.g. with a modified or added attribute) to someone else without changing the original configuration and without having to make a copy of all attributes. Here's how: `derived_from_x = (function(x){function y(){}; y.prototype=x; return new y();})(x);`. – 6502 Sep 20 '11 at 14:58
  • Your first argument is misleading: The readability issue of the multi-parameter version is not because of the pattern, but because of the poor modelling of the data. When the data modelling is right, the picture changes: Compare the signatures: `kick(user, reason, amount)`vs `kick(config)`. The config indirection feels like a good example of one too many. – ArnauOrriols Mar 16 '17 at 23:53
  • @ArnauOrriols: apparently we agree that we disagree. A function with 6 parameters in a language without keyword parameters is in my opinion ugly enough to require the configuration approach. – 6502 Mar 17 '17 at 06:54
  • My point is that a function with 6 parameters in a language without keyword parameters is not just ugly, it's actually a big smell of poor design. Fixing the ugliness with some cosmetics will only hide the real issue. In other words: if only taking into account the ugliness, yes, I agree that the configuration approach is better than the 6 parameter function. But a 3-well-modeled-parameters function is even better. – ArnauOrriols Mar 17 '17 at 15:05
2

You don't have to go strictly with any of the three. If you look at how jQuery does it, it examines the type and quantity and position of the parameters to figure out which overloaded flavor of the function is being used.

Suppose you had three flavors of kick(), one that takes person, reason and amount and one that takes just person with reason and amount getting default values and one that takes a config object with at least a person on it. You could dynamically see which of the three options you had like this:

function kick(person, reason, amount) {
    if (person.person) {
       // must be an object as the first parameter
       // extract the function parameters from that object
       amount = person.amount;
       reason = person.reason;
    }
    amount = amount || 5;           // apply default value if parameter wasn't passed
    reason = reason || "dislike";   // apply default value if parameter wasn't passed
    // now you have person, reason and amount and can do normal processing
    // you could have other parameters too
    // you just have to be to tell which parameter is which by type and position

    // process the kick here using person, reason and amount
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • A good way for providing flexibility to the function! However, will it cause inconsistencies/confusions in the function interface? (should this be a concern?) – Kenny Ki Sep 19 '11 at 06:56
  • There are no problems with using this type of javascript flexibility if it's done properly. jQuery is one of the most popular pieces of javascript on the web (use by more than 24 million sites and half of the top 10,000 sites) and it uses this technique extensively. – jfriend00 Sep 19 '11 at 14:46
1

JavaScript functions are signed by their name only.

Hence you can do:

  function kick(x, reason, amount) {
      if(reason && amount) {
          // do stuff with x as person, reason and amount
      }
      else if(x) {
          // do stuff with x as config
      }
      else {
         // do stuff with no parameters
      }

    }

another solutions is to use the arguments variable which is an array that holds all parameters that passed to a function in javascript

   function kick() {
            alert(arguments.length);
   }
aviv
  • 2,719
  • 7
  • 35
  • 48
  • 1
    One thing to watch out for when doing things like `if (reasons && amount)` is to make sure that they aren't parameters that can take a legitimate falsey value like false or 0 or "". If that is the case, then you have to explicitly check for undefined. – jfriend00 Sep 19 '11 at 06:13