8

How can I have a function accept either named arguments (foo({a: 'hello', b: 'it is me'})) or positional arguments (foo('hello', 'it is me'))?

I understand that named arguments can be simulated by passing an object to the function:

function foo(options) {
    options = options || {};
    var a = options.a || 'peanut'; // whatever default value
    var b = options.b || 'butter'; // whatever default value
    console.log(a, b);
}

// ES6 allows automatic destructuring
function foo({a = 'peanut', b = 'butter'} = {}) {
    console.log(a, b);
}

But that does not allow me to accept positional arguments to be passed.

I would like to use ES6 but anything from ES5 would be ok too.

4
  • You can detect the difference between your two scenarios, but ONLY if you know something about the expected type of the arguments and can test that to see which scenario was passed to the function. Commented Dec 17, 2015 at 6:24
  • @jfriend00 thought of checking whether the first argument is an Object to determine if named arguments where used but then what if I need a to be an Object as well. That would confuse both scenarios. Any ideas? Commented Dec 17, 2015 at 6:27
  • 2
    Javascript techniques for detecting different variable passing schemes depend upon types and quantity of arguments. You HAVE to design a scheme that lets you distinguish between the different possibilities. You can see How to Overload Functions in Javascript for a long discussion of the possibilities. Commented Dec 17, 2015 at 6:30
  • too much sugar. write good functions with explicit inputs. just because something might be possible with a specific language doesn't mean you should do it. Commented Dec 17, 2015 at 11:54

3 Answers 3

4

First of all, I really would recommend to stick with one approach. As you said, use either "named"

function foo({a = 'peanut', b = 'butter'} = {}) {
    console.log(a, b);
}

or positional arguments:

function foo(a = 'peanut', b = 'butter') {
    console.log(a, b);
}

Choose the one that fits your function better, do not mix both.


If you really need both for some reason, standard overloading techniques are available to you. It'll only work properly if your first positional argument is not an object. I would propose one of the following idioms:

function foo(a, b) { // positional is normal case
    if (arguments.length == 1 && typeof arguments[0] == "object")
        {a, b} = arguments[0];

    console.log(a, b);
}
function foo({a, b}) { // named is normal case
    if (arguments.length > 1 || typeof arguments[0] != "object")
        [a, b] = arguments;

    console.log(a, b);
}

and if you need default values, it gets ugly either way:

function foo(a, b) {
    var opts = (arguments.length == 1 && typeof arguments[0] == "object")
      ? arguments[0]
      : {a, b};
    ({a = 'peanut', b = 'butter'} = opts);

    console.log(a, b);
}
Sign up to request clarification or add additional context in comments.

Comments

1

I suppose something like this would work:

function foo(...options){
   if (typeof options[0] === 'object'){
    console.log('expect object', options[0]);
  }else{
    console.log('expect array', options);  
  }
}

foo('peanut', 'butter');
foo({a:'peanut', b:'butter'});

5 Comments

That makes sense. However, if I do foo('hello'), I would have options.length === 1. Then if I were to check if options[0] was an object, then foo({'whatever': 'object'}) would fail too. Any other ideas?
true, you could also check type of the first argument, if it is an object typeof
You're right but what if I expect a to be an object anyway? I would then pass an object for a but then it would then be wrongly recognized as a named argument set.
Also, typeof options[0] === 'object' is going to open up a can of worms: e.g. typeof null === 'object'
@AKG typeof new String() === "object" which is even worse given your parameter requirements
1

I don't think there's something built in for that, but this code should work for your case

function foo({a = 'peanut', b = 'butter'} = {}) {
    if (typeof arguments[0] === 'string') {
        return foo({a: arguments[0], b: arguments[1]})
    }
    console.log(a, b);
}

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.