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.
Advertisement
Answer
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); }