JavaScript doesn't allow supplying named parameters or any sort of parameter skipping, so it's not possible to do what you want with the function in its current form. Here are some alternatives, though:
Plain JavaScript approach: a configuration Object as parameter
Instead of accepting multiple parameters
func = (a, b, c) => { /* operate with parameters */ }
func("One", "Two", "Three")
your function will instead accept an object
func = config => { /* operate with config */ }
func({a: "One", b: "Two", c: "Three"})
This is a common pattern in JavaScript because it allows you to almost name your variables and doesn't require you pass them in the correct order.. It makes it easy to pass a large quantity of them and it can also make it easy to default them, too.
const doTheThing = (config) => {
const defaultProperties = {
b: "B",
c: "C"
}
const {a, b, c, ...rest} = Object.assign({}, defaultProperties, config);
const z = Object.values(rest); //extract their values, otherwise you get an object
console.log(a, b, c, z);
}
doTheThing({a: "A", x: "X", y: "Y", z: "Z"});
It is slightly clunky to use with rest parameters but not unworkable.
However, it does mean that it may be harder to see what parameters you can pass and what is required, if you have a large number of them.
Object Oriented approach: Builder pattern
You create a builder object - it serves to hold values until you call the final method at which point it takes all parameters and constructs an object in one go.
This is how more Object Oriented languages handle having a multitude of parameters where you can even have some of them optional. It's not really common to see builders defined like this in JavaScript but it's not too strange, either. If you use classes already or even TypeScript, then this is probably a better fit.
class DoTheThingBuilder {
constructor() {
this.a = null;
this.b = "B";
this.c = "C";
this.z = null;
}
withA(a) {
this.a = a;
return this;
}
withB(b) {
this.b = b;
return this;
}
withC(c) {
this.c = c;
return this;
}
withEverythingElse(...z) {
this.z = z;
return this;
}
doTheActualThing() {
const {a, b, c, z} = this;
console.log(a, b, c, z);
}
}
const builder = new DoTheThingBuilder();
builder
.withA("A")
.withEverythingElse("X", "Y", "Z")
.doTheActualThing();
As you can see, this can be pretty verbose for some simple tasks. It is a big overkill for this example, but perhaps in actual usage, you might find it helps.
I've deviated a bit from the usual approach - normally, you would set all parameters needed with the builder and finally call .build() which constructs an object. In this case, I basically renamed build to doTheActualThing and it's executing the function.
Functional approach: Currying
The concept of currying is quite simple - instead of having one function that accepts several parameters
func = (a, b, c) => { /* operate with parameters */ }
you have a function that takes one parameter, that returns a function that takes the second parameter, that returns another function, etc., until all parameters are satisfied, at which point the full function is executed.
func = a => b => c => { /* operate with parameters */ }
In many ways, this is the functional equivalent of the OO Builder pattern.
const doTheThing = (a) =>
(b = "B") =>
(c = 'C') =>
(...z) => console.log(a, b, c, z);
doTheThing("A")()()("X", "Y", "Z");
This way you can skip the second and third parameter by not supplying them and you'd get the defaults. It's also way shorter than a builder. However, reading the function can be a bit weird.