AKG
AKG

Reputation: 3296

Allow either named arguments or positional arguments in Javascript

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.

Upvotes: 8

Views: 2865

Answers (3)

Bergi
Bergi

Reputation: 664970

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);
}

Upvotes: 4

gafi
gafi

Reputation: 12784

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);
}

Upvotes: 1

glued
glued

Reputation: 2639

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'});

Upvotes: 1

Related Questions